0

Reland "Migrate from IPP media/media-supported to media-col/media-col-database"

This is a reland of commit 7635617eba

Fixes a few printing UI tests that were failing on Mac.

Original change's description:
> Migrate from IPP media/media-supported to media-col/media-col-database
>
> Effects of getting the supported paper sizes from CUPS using
> media-col-database instead of media-supported:
> * We get paper sizes as actual dimensions in PWG units instead of having
>   to parse them from strings.
> * The vendor ID for each paper size is now determined from its
>   dimensions instead of the other way around.
> * Paper display names are now based solely on dimensions, so we can end
>   the long game of whack-a-mole trying to get non-standard and duplicate
>   paper sizes to display and sort properly.
> * When de-duplicating sizes with borderless variants, we now determine
>   whether a size is borderless using its actual margins instead of
>   vendor ID heuristics.
> * Dimensions are now formatted properly for the current locale when
>   generating display names for unregistered sizes.
> * The algorithm used to determine whether an unregistered size is inches
>   or millimeters is better than the one used in CUPS, accounting for
>   eighths and tenths of an inch. For example, the size that used to
>   display as "200.03 x 148.17 mm" is now properly rendered as
>   "7.875 x 5.833 in".
>
> Effects of sending the paper size for a print job using media-col
> instead of media:
> * We now send the "media-source" (input tray) as part of media-col, in
>   accordance with IPP standards. Before, we had to use the standalone
>   "media-source" attribute, which was a non-standard extension
>   implemented in the ChromeOS fork of CUPS.
> * Sending media-col opens the way for work to begin in earnest on media
>   type support and borderless printing.
> * We no longer use libcups' janky cupsDestMedia* functions for anything.
>
> Bug: b:199547118
> Test: do print jobs and verify the size, source, and margins are correct
> Test: check the paper size list in the UI for various printers
> Change-Id: I9030283ae22d1f71b20f0237e664af4943fc58c4
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4257505
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Commit-Queue: Bryan Cain <bryancain@chromium.org>
> Reviewed-by: Benjamin Gordon <bmgordon@chromium.org>
> Code-Coverage: Findit <findit-for-me@appspot.gserviceaccount.com>
> Cr-Commit-Position: refs/heads/main@{#1135611}

Bug: b:199547118
Change-Id: I505e3b82a560b0128c4526acd5ae6c039ee3ab58
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4517541
Reviewed-by: Benjamin Gordon <bmgordon@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Bryan Cain <bryancain@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1144269}
This commit is contained in:
Bryan Cain
2023-05-15 18:52:31 +00:00
committed by Chromium LUCI CQ
parent 26cba3f8c6
commit fef8037fe5
24 changed files with 1674 additions and 1385 deletions

@ -65,6 +65,12 @@ std::u16string FormatNumber(int64_t number) {
}
std::u16string FormatDouble(double number, int fractional_digits) {
return FormatDouble(number, fractional_digits, fractional_digits);
}
std::u16string FormatDouble(double number,
int min_fractional_digits,
int max_fractional_digits) {
icu::NumberFormat* number_format =
g_number_format_float.Get().number_format.get();
@ -72,8 +78,8 @@ std::u16string FormatDouble(double number, int fractional_digits) {
// As a fallback, just return the raw number in a string.
return ASCIIToUTF16(StringPrintf("%f", number));
}
number_format->setMaximumFractionDigits(fractional_digits);
number_format->setMinimumFractionDigits(fractional_digits);
number_format->setMaximumFractionDigits(max_fractional_digits);
number_format->setMinimumFractionDigits(min_fractional_digits);
icu::UnicodeString ustr;
number_format->format(number, ustr);

@ -17,12 +17,24 @@ namespace base {
// Ex: FormatNumber(1234567) => "1,234,567" in English, "1.234.567" in German
BASE_I18N_EXPORT std::u16string FormatNumber(int64_t number);
// Return a number formatted with separators in the user's locale.
// Return a number formatted with separators in the user's locale, with
// `fractional_digits` digits after the decimal point.
// Ex: FormatDouble(1234567.8, 1)
// => "1,234,567.8" in English, "1.234.567,8" in German
BASE_I18N_EXPORT std::u16string FormatDouble(double number,
int fractional_digits);
// Return a number formatted with separators in the user's locale, with up to
// `max_fractional_digits` digits after the decimal point, and eliminating
// trailing zeroes after `min_fractional_digits`.
// Ex: FormatDouble(1234567.8, 0, 4)
// => "1,234,567.8" in English, "1.234.567,8" in German
// Ex: FormatDouble(1234567.888888, 0, 4)
// => "1,234,567.8889" in English, "1.234.567,8889" in German
BASE_I18N_EXPORT std::u16string FormatDouble(double number,
int min_fractional_digits,
int max_fractional_digits);
// Return a percentage formatted with space and symbol in the user's locale.
// Ex: FormatPercent(12) => "12%" in English, "12 %" in Romanian
BASE_I18N_EXPORT std::u16string FormatPercent(int number);

@ -45,7 +45,7 @@ TEST(NumberFormattingTest, FormatNumber) {
}
}
TEST(NumberFormattingTest, FormatDouble) {
TEST(NumberFormattingTest, FormatDoubleWithFixedFractionalDigits) {
static const struct {
double number;
int frac_digits;
@ -91,6 +91,55 @@ TEST(NumberFormattingTest, FormatDouble) {
}
}
TEST(NumberFormattingTest, FormatDoubleWithFractionalDigitRange) {
static const struct {
double number;
int min_frac_digits;
int max_frac_digits;
const char* expected_english;
const char* expected_german;
} cases[] = {
{0.0, 0, 0, "0", "0"},
#if !BUILDFLAG(IS_ANDROID)
// Bionic can't printf negative zero correctly.
{-0.0, 0, 4, "-0", "-0"},
#endif
{1024.2, 0, 0, "1,024", "1.024"},
{-1024.223, 0, 2, "-1,024.22", "-1.024,22"},
{std::numeric_limits<double>::max(), 0, 6,
"179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,"
"000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
"000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
"000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
"000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
"000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
"000",
"179.769.313.486.231.570.000.000.000.000.000.000.000.000.000.000.000."
"000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
"000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
"000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
"000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
"000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
"000"},
{std::numeric_limits<double>::min(), 2, 2, "0.00", "0,00"},
{-42.7, 0, 3, "-42.7", "-42,7"},
};
test::ScopedRestoreICUDefaultLocale restore_locale;
for (const auto& i : cases) {
i18n::SetICUDefaultLocale("en");
ResetFormattersForTesting();
EXPECT_EQ(i.expected_english,
UTF16ToUTF8(FormatDouble(i.number, i.min_frac_digits,
i.max_frac_digits)));
i18n::SetICUDefaultLocale("de");
ResetFormattersForTesting();
EXPECT_EQ(i.expected_german,
UTF16ToUTF8(FormatDouble(i.number, i.min_frac_digits,
i.max_frac_digits)));
}
}
TEST(NumberFormattingTest, FormatPercent) {
static const struct {
int64_t number;

@ -238,8 +238,7 @@ std::unique_ptr<printing::PrintSettings> ParsePrintTicket(
}
cloud_devices::printer::Media media_value = media.value();
printing::PrintSettings::RequestedMedia requested_media;
if (media_value.size_um.width() <= 0 || media_value.size_um.height() <= 0 ||
media_value.vendor_id.empty()) {
if (media_value.size_um.width() <= 0 || media_value.size_um.height() <= 0) {
LOG(ERROR) << "Loaded invalid media from print ticket.";
return nullptr;
}
@ -306,8 +305,7 @@ bool CheckSettingsAndCapabilitiesCompatibility(
capabilities.papers,
[&requested_media](
const printing::PrinterSemanticCapsAndDefaults::Paper& paper) {
return paper.size_um == requested_media.size_microns &&
paper.vendor_id == requested_media.vendor_id;
return paper.size_um == requested_media.size_microns;
});
}

@ -905,9 +905,16 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
print_view_manager.snooped_params();
ASSERT_TRUE(snooped_params);
EXPECT_EQ(test::kPrinterCapabilitiesDpi, snooped_params->params->dpi);
#if BUILDFLAG(IS_MAC)
EXPECT_EQ(kLegalPhysicalSize, snooped_params->params->page_size);
EXPECT_EQ(kLegalPrintableArea, snooped_params->params->printable_area);
EXPECT_EQ(kLegalExpectedContentSize, snooped_params->params->content_size);
#else
EXPECT_EQ(kLetterPhysicalSize, snooped_params->params->page_size);
EXPECT_EQ(kLetterPrintableArea, snooped_params->params->printable_area);
EXPECT_EQ(kLetterExpectedContentSize, snooped_params->params->content_size);
#endif
}
#if BUILDFLAG(ENABLE_OOP_PRINTING)
@ -935,9 +942,16 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
print_view_manager.snooped_params();
ASSERT_TRUE(snooped_params);
EXPECT_EQ(test::kPrinterCapabilitiesDpi, snooped_params->params->dpi);
#if BUILDFLAG(IS_MAC)
EXPECT_EQ(kLetterPhysicalSize, snooped_params->params->page_size);
EXPECT_EQ(kLetterPrintableArea, snooped_params->params->printable_area);
EXPECT_EQ(kLetterExpectedContentSize, snooped_params->params->content_size);
#else
EXPECT_EQ(kLegalPhysicalSize, snooped_params->params->page_size);
EXPECT_EQ(kLegalPrintableArea, snooped_params->params->printable_area);
EXPECT_EQ(kLegalExpectedContentSize, snooped_params->params->content_size);
#endif
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,

File diff suppressed because it is too large Load Diff

@ -19,9 +19,9 @@ enum class MediaSizeGroup {
};
struct MediaSizeInfo {
std::u16string name;
std::string vendor_id;
std::u16string display_name;
MediaSizeGroup sort_group;
bool registered_size;
};
struct PaperWithSizeInfo {
@ -31,13 +31,12 @@ struct PaperWithSizeInfo {
PrinterSemanticCapsAndDefaults::Paper paper;
};
// Maps a paper vendor ID to a localized name and sort group. The returned name
// will be automatically generated if the vendor ID does not have a known
// mapping. If the vendor ID is not a valid PWG self-describing media name,
// the returned name will be empty. The returned names are u16strings to
// facilitate subsequent sorting; they need to be converted to UTF-8 before
// updating a `Paper` object.
MediaSizeInfo LocalizePaperDisplayName(const std::string& vendor_id);
// Maps a paper size to a vendor ID, localized display name and sort group. The
// returned ID and name will be automatically generated if the size does not
// have a known mapping. The display names are u16strings to facilitate
// subsequent sorting; they need to be converted to UTF-8 before updating a
// `Paper` object.
MediaSizeInfo LocalizePaperDisplayName(const gfx::Size& size_um);
// Sorts a list of paper sizes in place by using the paired sort groups.
void SortPaperDisplayNames(std::vector<PaperWithSizeInfo>& papers);

@ -21,38 +21,62 @@ using Paper = PrinterSemanticCapsAndDefaults::Paper;
namespace {
struct MediaInfoTestCase {
const char* vendor_id;
gfx::Size size_um;
std::string expected_vendor_id;
std::u16string expected_localized_name;
MediaSizeGroup expected_group;
};
void VerifyLocalizedInfo(const MediaInfoTestCase& test_case) {
MediaSizeInfo info = LocalizePaperDisplayName(test_case.vendor_id);
EXPECT_EQ(info.name, test_case.expected_localized_name);
MediaSizeInfo info = LocalizePaperDisplayName(test_case.size_um);
EXPECT_EQ(info.vendor_id, test_case.expected_vendor_id);
EXPECT_EQ(info.display_name, test_case.expected_localized_name);
EXPECT_EQ(info.sort_group, test_case.expected_group);
}
void VerifyPaperSizeMatch(const PaperWithSizeInfo& lhs,
const PaperWithSizeInfo& rhs) {
EXPECT_EQ(lhs.size_info.name, rhs.size_info.name);
EXPECT_EQ(lhs.size_info.vendor_id, rhs.size_info.vendor_id);
EXPECT_EQ(lhs.size_info.display_name, rhs.size_info.display_name);
EXPECT_EQ(lhs.size_info.sort_group, rhs.size_info.sort_group);
EXPECT_EQ(lhs.paper, rhs.paper);
}
} // namespace
// Verifies that we localize some common names.
TEST(PrintMediaL10N, LocalizeSomeCommonNames) {
// Verifies that we localize some common paper sizes.
TEST(PrintMediaL10N, LocalizeSomeCommonSizes) {
const MediaInfoTestCase kTestCases[] = {
{"na_c_17x22in", u"17 x 22 in", MediaSizeGroup::kSizeIn},
{"iso_a0_841x1189mm", u"A0", MediaSizeGroup::kSizeNamed},
{"iso_a1_594x841mm", u"A1", MediaSizeGroup::kSizeNamed},
{"iso_a4_210x297mm", u"A4", MediaSizeGroup::kSizeNamed},
{"na_govt-legal_8x13in", u"8 x 13 in", MediaSizeGroup::kSizeIn},
{"na_govt-letter_8x10in", u"8 x 10 in", MediaSizeGroup::kSizeIn},
{"na_letter_8.5x11in", u"Letter", MediaSizeGroup::kSizeNamed},
{"oe_photo-l_3.5x5in", u"3.5 x 5 in", MediaSizeGroup::kSizeIn},
{"om_business-card_55x91mm", u"55 x 91 mm", MediaSizeGroup::kSizeMm},
{{431800, 558800},
"na_c_17x22in",
u"17 x 22 in",
MediaSizeGroup::kSizeIn},
{{841000, 1189000},
"iso_a0_841x1189mm",
u"A0",
MediaSizeGroup::kSizeNamed},
{{594000, 841000}, "iso_a1_594x841mm", u"A1", MediaSizeGroup::kSizeNamed},
{{210000, 297000}, "iso_a4_210x297mm", u"A4", MediaSizeGroup::kSizeNamed},
{{203200, 330200},
"na_govt-legal_8x13in",
u"8 x 13 in",
MediaSizeGroup::kSizeIn},
{{203200, 254000},
"na_govt-letter_8x10in",
u"8 x 10 in",
MediaSizeGroup::kSizeIn},
{{215900, 279400},
"na_letter_8.5x11in",
u"Letter",
MediaSizeGroup::kSizeNamed},
{{88900, 127000},
"oe_photo-l_3.5x5in",
u"3.5 x 5 in",
MediaSizeGroup::kSizeIn},
{{55000, 91000},
"om_business-card_55x91mm",
u"55 x 91 mm",
MediaSizeGroup::kSizeMm},
};
for (const auto& test_case : kTestCases) {
@ -60,66 +84,38 @@ TEST(PrintMediaL10N, LocalizeSomeCommonNames) {
}
}
// Verifies that we return the empty string when no localization is
// found for a given media name.
TEST(PrintMediaL10N, DoWithoutCommonName) {
// Verifies that we generate a sensible vendor ID and display name when no
// localization is found for a given media size.
TEST(PrintMediaL10N, LocalizeNonStandardSizes) {
const MediaInfoTestCase kTestCases[] = {
{"", u"", MediaSizeGroup::kSizeNamed},
{"lorem_ipsum_8x10", u"", MediaSizeGroup::kSizeNamed},
{"q_e_d_130x200mm", u"", MediaSizeGroup::kSizeNamed},
{"not at all a valid vendor ID", u"", MediaSizeGroup::kSizeNamed},
};
for (const auto& test_case : kTestCases) {
VerifyLocalizedInfo(test_case);
}
}
// Verifies that duplicates have the same localization.
TEST(PrintMediaL10N, LocalizeDuplicateNames) {
const struct {
const char* duplicate_vendor_id;
const char* vendor_id;
} kTestCases[] = {
{"oe_photo-s10r_10x15in", "na_10x15_10x15in"},
{"om_large-photo_200x300", "om_large-photo_200x300mm"},
{"om_postfix_114x229mm", "iso_c6c5_114x229mm"},
{"prc_10_324x458mm", "iso_c3_324x458mm"},
{"prc_3_125x176mm", "iso_b6_125x176mm"},
{"prc_5_110x220mm", "iso_dl_110x220mm"},
{"iso_id-3_88x125mm", "iso_b7_88x125mm"},
{"na_letter_8.5x11in", "na_card-letter_8.5x11in"},
{"na_letter_8.5x11in", "na_letter.fb_8.5x11in"},
{"na_letter_8.5x11in", "na_card-letter.fb_8.5x11in"},
};
for (const auto& test_case : kTestCases) {
MediaSizeInfo duplicate =
LocalizePaperDisplayName(test_case.duplicate_vendor_id);
MediaSizeInfo original = LocalizePaperDisplayName(test_case.vendor_id);
EXPECT_EQ(duplicate.name, original.name);
EXPECT_EQ(duplicate.sort_group, original.sort_group);
}
}
// Verifies that we generate names for unrecognized sizes correctly.
TEST(PrintMediaL10N, LocalizeSelfDescribingSizes) {
const MediaInfoTestCase kTestCases[] = {
{"invalid_size", u"", MediaSizeGroup::kSizeNamed},
{"om_photo-31x41_310x410mm", u"310 x 410 mm", MediaSizeGroup::kSizeMm},
{"om_t-4-x-7_4x7in", u"4 x 7 in", MediaSizeGroup::kSizeIn},
{"om_4-x-7_101.6x180.6mm", u"4 X 7 (101.6 x 180.6 mm)",
MediaSizeGroup::kSizeNamed},
{"om_custom-1_209.9x297.04mm", u"Custom 1 (209.9 x 297.04 mm)",
MediaSizeGroup::kSizeNamed},
{"om_double-postcard-rotated_200.03x148.17mm",
u"Double Postcard Rotated (200.03 x 148.17 mm)",
MediaSizeGroup::kSizeNamed},
{"oe_photo-8x10-tab_8x10.5in", u"Photo 8x10 Tab (8 x 10.5 in)",
MediaSizeGroup::kSizeNamed},
{"na_card-letter_8.5x11in", u"Letter", MediaSizeGroup::kSizeNamed},
{"na_letter.fb_8.5x11in", u"Letter", MediaSizeGroup::kSizeNamed},
{{310000, 410000},
"om_310000x410000um_310x410mm",
u"310 x 410 mm",
MediaSizeGroup::kSizeMm},
{{101600, 177800},
"om_101600x177800um_101x177mm",
u"4 x 7 in",
MediaSizeGroup::kSizeIn},
{{101600, 180620},
"om_101600x180620um_101x180mm",
u"4 x 7.111 in",
MediaSizeGroup::kSizeIn},
{{209900, 297040},
"om_209900x297040um_209x297mm",
u"210 x 297 mm",
MediaSizeGroup::kSizeMm},
{{200030, 148170},
"om_200030x148170um_200x148mm",
u"200 x 148 mm",
MediaSizeGroup::kSizeMm},
{{203200, 266700},
"om_203200x266700um_203x266mm",
u"8 x 10.5 in",
MediaSizeGroup::kSizeIn},
{{133350, 180620},
"om_133350x180620um_133x180mm",
u"5.25 x 7.111 in",
MediaSizeGroup::kSizeIn},
};
for (const auto& test_case : kTestCases) {
@ -129,15 +125,13 @@ TEST(PrintMediaL10N, LocalizeSelfDescribingSizes) {
// Verifies that paper sizes are returned in the expected order of groups.
TEST(PrintMediaL10N, SortGroupsOrdered) {
PaperWithSizeInfo mm = {
MediaSizeInfo{u"mm", MediaSizeGroup::kSizeMm, /*registered_size=*/false},
Paper{"metric", "mm", gfx::Size()}};
PaperWithSizeInfo in = {
MediaSizeInfo{u"in", MediaSizeGroup::kSizeIn, /*registered_size=*/false},
Paper{"inches", "in", gfx::Size()}};
PaperWithSizeInfo named = {MediaSizeInfo{u"named", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
Paper{"named size", "named", gfx::Size()}};
PaperWithSizeInfo mm = {MediaSizeInfo{"", u"mm", MediaSizeGroup::kSizeMm},
Paper{"metric", "mm", gfx::Size()}};
PaperWithSizeInfo in = {MediaSizeInfo{"", u"in", MediaSizeGroup::kSizeIn},
Paper{"inches", "in", gfx::Size()}};
PaperWithSizeInfo named = {
MediaSizeInfo{"", u"named", MediaSizeGroup::kSizeNamed},
Paper{"named size", "named", gfx::Size()}};
std::vector<PaperWithSizeInfo> papers = {mm, named, in};
std::vector<PaperWithSizeInfo> expected = {in, mm, named};
@ -147,48 +141,40 @@ TEST(PrintMediaL10N, SortGroupsOrdered) {
}
}
// Verifies that inch paper sizes are sorted by width, height, name.
// Verifies that inch paper sizes are sorted by width, then height.
TEST(PrintMediaL10N, SortInchSizes) {
PaperWithSizeInfo p1 = {
MediaSizeInfo{u"1x3", MediaSizeGroup::kSizeIn, /*registered_size=*/false},
MediaSizeInfo{"", u"1x3", MediaSizeGroup::kSizeIn},
Paper{"1x3", "in", gfx::Size(1, 3), gfx::Rect(0, 0, 1, 3)}};
PaperWithSizeInfo p2 = {
MediaSizeInfo{u"2x1", MediaSizeGroup::kSizeIn, /*registered_size=*/false},
MediaSizeInfo{"", u"2x1", MediaSizeGroup::kSizeIn},
Paper{"2x1", "in", gfx::Size(2, 1), gfx::Rect(0, 0, 2, 1)}};
PaperWithSizeInfo p3 = {
MediaSizeInfo{u"2x2", MediaSizeGroup::kSizeIn, /*registered_size=*/false},
MediaSizeInfo{"", u"2x2", MediaSizeGroup::kSizeIn},
Paper{"2x2", "in", gfx::Size(2, 2), gfx::Rect(0, 0, 2, 2)}};
PaperWithSizeInfo p4 = {
MediaSizeInfo{u"2x2 B", MediaSizeGroup::kSizeIn,
/*registered_size=*/false},
Paper{"2x2 B", "in", gfx::Size(2, 2), gfx::Rect(0, 0, 2, 2)}};
std::vector<PaperWithSizeInfo> papers = {p4, p1, p2, p3};
std::vector<PaperWithSizeInfo> expected = {p1, p2, p3, p4};
std::vector<PaperWithSizeInfo> papers = {p2, p3, p1};
std::vector<PaperWithSizeInfo> expected = {p1, p2, p3};
SortPaperDisplayNames(papers);
for (size_t i = 0; i < expected.size(); i++) {
VerifyPaperSizeMatch(papers[i], expected[i]);
}
}
// Verifies that mm paper sizes are sorted by width, height, name.
// Verifies that mm paper sizes are sorted by width, then height.
TEST(PrintMediaL10N, SortMmSizes) {
PaperWithSizeInfo p1 = {
MediaSizeInfo{u"1x3", MediaSizeGroup::kSizeMm, /*registered_size=*/false},
MediaSizeInfo{"", u"1x3", MediaSizeGroup::kSizeMm},
Paper{"1x3", "mm", gfx::Size(1, 3), gfx::Rect(0, 0, 1, 3)}};
PaperWithSizeInfo p2 = {
MediaSizeInfo{u"2x1", MediaSizeGroup::kSizeMm, /*registered_size=*/false},
MediaSizeInfo{"", u"2x1", MediaSizeGroup::kSizeMm},
Paper{"2x1", "mm", gfx::Size(2, 1), gfx::Rect(0, 0, 2, 1)}};
PaperWithSizeInfo p3 = {
MediaSizeInfo{u"2x2", MediaSizeGroup::kSizeMm, /*registered_size=*/false},
MediaSizeInfo{"", u"2x2", MediaSizeGroup::kSizeMm},
Paper{"2x2", "mm", gfx::Size(2, 2), gfx::Rect(0, 0, 2, 2)}};
PaperWithSizeInfo p4 = {
MediaSizeInfo{u"2x2 B", MediaSizeGroup::kSizeMm,
/*registered_size=*/false},
Paper{"2x2 B", "mm", gfx::Size(2, 2), gfx::Rect(0, 0, 2, 2)}};
std::vector<PaperWithSizeInfo> papers = {p4, p1, p2, p3};
std::vector<PaperWithSizeInfo> expected = {p1, p2, p3, p4};
std::vector<PaperWithSizeInfo> papers = {p2, p3, p1};
std::vector<PaperWithSizeInfo> expected = {p1, p2, p3};
SortPaperDisplayNames(papers);
for (size_t i = 0; i < expected.size(); i++) {
VerifyPaperSizeMatch(papers[i], expected[i]);
@ -198,20 +184,16 @@ TEST(PrintMediaL10N, SortMmSizes) {
// Verifies that named paper sizes are sorted by name, width, height.
TEST(PrintMediaL10N, SortNamedSizes) {
PaperWithSizeInfo p1 = {
MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
MediaSizeInfo{"", u"AAA", MediaSizeGroup::kSizeNamed},
Paper{"AAA", "name", gfx::Size(50, 50), gfx::Rect(0, 0, 50, 50)}};
PaperWithSizeInfo p2 = {
MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
MediaSizeInfo{"", u"BBB", MediaSizeGroup::kSizeNamed},
Paper{"BBB", "name1", gfx::Size(1, 3), gfx::Rect(0, 0, 1, 3)}};
PaperWithSizeInfo p3 = {
MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
MediaSizeInfo{"", u"BBB", MediaSizeGroup::kSizeNamed},
Paper{"BBB", "name2", gfx::Size(2, 2), gfx::Rect(0, 0, 2, 2)}};
PaperWithSizeInfo p4 = {
MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
MediaSizeInfo{"", u"BBB", MediaSizeGroup::kSizeNamed},
Paper{"BBB", "name3", gfx::Size(2, 3), gfx::Rect(0, 0, 2, 3)}};
std::vector<PaperWithSizeInfo> papers = {p4, p1, p2, p3};
@ -222,59 +204,4 @@ TEST(PrintMediaL10N, SortNamedSizes) {
}
}
TEST(PrintMediaL10N, RemoveBorderlessSizes) {
PaperWithSizeInfo p1 = {MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
Paper{"AAA", "oe_aaa.fb_8x10in", gfx::Size(8, 10)}};
PaperWithSizeInfo p2 = {MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed},
Paper{"BBB", "oe_bbb_4x6in", gfx::Size(4, 6)}};
PaperWithSizeInfo p3 = {
MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed},
Paper{"BBB", "oe_bbb.borderless_4x6in", gfx::Size(4, 6)}};
PaperWithSizeInfo p4 = {MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
Paper{"AAA", "oe_aaa.8x10in", gfx::Size(8, 10)}};
std::vector<PaperWithSizeInfo> papers = {p1, p2, p3, p4};
std::vector<PaperWithSizeInfo> expected = {p4, p2};
SortPaperDisplayNames(papers);
ASSERT_EQ(papers.size(), expected.size());
for (size_t i = 0; i < expected.size(); i++) {
VerifyPaperSizeMatch(papers[i], expected[i]);
}
}
// Verifies that PWG registered size names sort above unregistered names with
// the same dimensions.
TEST(PrintMediaL10N, SortNonstandardSizes) {
PaperWithSizeInfo p1 = {
MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
Paper{"AAA", "na_card-letter_8.5x11in", gfx::Size(9, 11)}};
PaperWithSizeInfo p2 = {
MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed,
/*registered_size=*/false},
Paper{"AAA", "na_card-letter_8x9in", gfx::Size(8, 11)}};
PaperWithSizeInfo p3 = {
MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeIn, /*registered_size=*/false},
Paper{"AAA", "oe_aaa.8x10in", gfx::Size(8, 10)}};
PaperWithSizeInfo p4 = {MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed,
/*registered_size=*/true},
Paper{"AAA", "na_letter_8.5x11in", gfx::Size(9, 11)}};
PaperWithSizeInfo p5 = {
MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeIn, /*registered_size=*/true},
Paper{"BBB", "na_govt-letter_8x10in", gfx::Size(8, 10)}};
PaperWithSizeInfo p6 = {
MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeIn, /*registered_size=*/true},
Paper{"AAA", "na_govt-letter_8x10in", gfx::Size(8, 10)}};
std::vector<PaperWithSizeInfo> papers = {p1, p2, p3, p4, p5, p6};
std::vector<PaperWithSizeInfo> expected = {p6, p5, p3, p4, p2, p1};
SortPaperDisplayNames(papers);
ASSERT_EQ(papers.size(), expected.size());
for (size_t i = 0; i < expected.size(); i++) {
VerifyPaperSizeMatch(papers[i], expected[i]);
}
}
} // namespace printing

@ -59,22 +59,19 @@ namespace {
#if BUILDFLAG(PRINT_MEDIA_L10N_ENABLED)
// Iterate on the `Papers` of a given printer `info` and set the
// `display_name` members, localizing where possible. We expect the
// backend to have populated non-empty display names already, so we
// don't touch media display names that we can't localize.
// The `Papers` will be sorted in place when this function returns.
void PopulateAndSortAllPaperDisplayNames(PrinterSemanticCapsAndDefaults& info) {
MediaSizeInfo default_paper_display =
LocalizePaperDisplayName(info.default_paper.vendor_id);
if (!default_paper_display.name.empty()) {
info.default_paper.display_name =
base::UTF16ToUTF8(default_paper_display.name);
}
// `display_name` members, localizing where possible, as well as the `vendor_id`
// members. The `Papers` will be sorted in place when this function returns.
void PopulateAndSortAllPaperNames(PrinterSemanticCapsAndDefaults& info) {
MediaSizeInfo default_paper =
LocalizePaperDisplayName(info.default_paper.size_um);
info.default_paper.vendor_id = default_paper.vendor_id;
info.default_paper.display_name =
base::UTF16ToUTF8(default_paper.display_name);
// Pair the paper entries with their sort info so they can be sorted.
std::vector<PaperWithSizeInfo> size_list;
for (PrinterSemanticCapsAndDefaults::Paper& paper : info.papers) {
size_list.emplace_back(LocalizePaperDisplayName(paper.vendor_id),
size_list.emplace_back(LocalizePaperDisplayName(paper.size_um),
std::move(paper));
}
@ -83,9 +80,8 @@ void PopulateAndSortAllPaperDisplayNames(PrinterSemanticCapsAndDefaults& info) {
info.papers.clear();
for (auto& pair : size_list) {
auto& paper = info.papers.emplace_back(std::move(pair.paper));
if (!pair.size_info.name.empty()) {
paper.display_name = base::UTF16ToUTF8(pair.size_info.name);
}
paper.vendor_id = pair.size_info.vendor_id;
paper.display_name = base::UTF16ToUTF8(pair.size_info.display_name);
}
}
#endif // BUILDFLAG(PRINT_MEDIA_L10N_ENABLED)
@ -119,16 +115,17 @@ base::Value AssemblePrinterCapabilities(const std::string& device_name,
return base::Value();
#if BUILDFLAG(PRINT_MEDIA_L10N_ENABLED)
bool populate_paper_display_names = true;
bool populate_paper_names = true;
#if BUILDFLAG(IS_MAC)
// Paper display name localization requires standardized vendor ID names
// populated by CUPS IPP. If the CUPS IPP backend is not enabled, localization
// will not properly occur.
populate_paper_display_names =
// Paper display name localization and vendor ID assignment is intended for
// use with the CUPS IPP backend. If the CUPS IPP backend is not enabled,
// localization will not properly occur.
populate_paper_names =
base::FeatureList::IsEnabled(features::kCupsIppPrintingBackend);
#endif
if (populate_paper_display_names)
PopulateAndSortAllPaperDisplayNames(*caps);
if (populate_paper_names) {
PopulateAndSortAllPaperNames(*caps);
}
#endif // BUILDFLAG(PRINT_MEDIA_L10N_ENABLED)
#if BUILDFLAG(IS_CHROMEOS)

@ -206,7 +206,11 @@ TEST_F(PrinterCapabilitiesTest, UserDefinedPapers) {
// Verify the 3 paper sizes are the ones in |caps->papers|, followed by the
// ones in |user_defined_papers|.
#if BUILDFLAG(PRINT_MEDIA_L10N_ENABLED)
VerifyPaper((*media_option)[0], "0 x 0 mm", "om_100x234um_0x0mm", {100, 234});
#else
VerifyPaper((*media_option)[0], "printer_foo", "printer_vendor", {100, 234});
#endif
VerifyPaper((*media_option)[1], "foo", "vendor", {200, 300});
VerifyPaper((*media_option)[2], "bar", "vendor", {600, 600});
}
@ -272,7 +276,8 @@ TEST_F(PrinterCapabilitiesTest, PaperLocalizationsApplied) {
// Verify the paper sizes are the ones in `caps->papers` in the correct
// order.
VerifyPaper((*media_option)[0], "2 x 3 in", "oe_2x3_2x3in", {50800, 76200});
VerifyPaper((*media_option)[0], "2 x 3 in", "om_50800x76200um_50x76mm",
{50800, 76200});
VerifyPaper((*media_option)[1], "3.5 x 5 in", "oe_photo-l_3.5x5in",
{88900, 127000});
VerifyPaper((*media_option)[2], "4 x 4 in", "oe_square-photo_4x4in",
@ -281,18 +286,19 @@ TEST_F(PrinterCapabilitiesTest, PaperLocalizationsApplied) {
{101600, 152400});
VerifyPaper((*media_option)[4], "5 x 5 in", "oe_square-photo_5x5in",
{127000, 127000});
VerifyPaper((*media_option)[5], "1 x 1 mm", "om_1-x-1_1x1mm", {1000, 1000});
VerifyPaper((*media_option)[6], "210 x 330 mm", "om_folio_210x330mm",
VerifyPaper((*media_option)[5], "9 x 12.1 in", "om_228600x307340um_228x307mm",
{228600, 307340});
VerifyPaper((*media_option)[6], "1 x 1 mm", "om_1000x1000um_1x1mm",
{1000, 1000});
VerifyPaper((*media_option)[7], "210 x 330 mm", "om_folio_210x330mm",
{210000, 330000});
VerifyPaper((*media_option)[7], "215 x 315 mm", "om_folio-sp_215x315mm",
VerifyPaper((*media_option)[8], "215 x 315 mm", "om_folio-sp_215x315mm",
{215000, 315000});
VerifyPaper((*media_option)[8], "215 x 400 mm", "om_photo-21x40_215x400mm",
{215000, 400000});
VerifyPaper((*media_option)[9], "A4", "iso_a4_210x297mm", {210000, 297000});
VerifyPaper((*media_option)[10], "Custom 1 (9 x 12.1 in)",
"na_custom-1_9x12.1in", {228600, 307340});
VerifyPaper((*media_option)[11], "Custom 2 (299.6 x 405.3 mm)",
"na_custom-2_299.6x405.3mm", {299600, 405300});
VerifyPaper((*media_option)[9], "215 x 400 mm",
"om_215000x400000um_215x400mm", {215000, 400000});
VerifyPaper((*media_option)[10], "300 x 405 mm",
"om_299600x405300um_299x405mm", {299600, 405300});
VerifyPaper((*media_option)[11], "A4", "iso_a4_210x297mm", {210000, 297000});
VerifyPaper((*media_option)[12], "Letter", "na_letter_8.5x11in",
{215900, 279400});
VerifyPaper((*media_option)[13], "foo", "vendor", {200, 300});

@ -19,13 +19,18 @@ constexpr char kIppLastDocument[] = "last-document"; // RFC 8011
constexpr char kIppPin[] = "job-password"; // PWG 5100.11
constexpr char kIppPinEncryption[] = "job-password-encryption"; // PWG 5100.11
constexpr char kIppPrinterUri[] = "printer-uri"; // RFC 8011
constexpr char kIppRequestedAttributes[] = "requested-attributes"; // RFC 8011
constexpr char kIppRequestingUserName[] = "requesting-user-name"; // RFC 8011
// printer attributes
constexpr char kIppMediaColDatabase[] = "media-col-database";
// job attributes
constexpr char kIppCollate[] = "multiple-document-handling"; // PWG 5100.19
constexpr char kIppCopies[] = CUPS_COPIES;
constexpr char kIppColor[] = CUPS_PRINT_COLOR_MODE;
constexpr char kIppMedia[] = CUPS_MEDIA;
constexpr char kIppMediaCol[] = "media-col"; // PWG 5100.7
constexpr char kIppDuplex[] = CUPS_SIDES;
constexpr char kIppResolution[] = "printer-resolution"; // RFC 8011
@ -33,6 +38,16 @@ constexpr char kIppResolution[] = "printer-resolution"; // RFC 8011
constexpr char kCollated[] = "separate-documents-collated-copies";
constexpr char kUncollated[] = "separate-documents-uncollated-copies";
// media-col collection members (all from PWG 5100.7)
constexpr char kIppMediaBottomMargin[] = "media-bottom-margin";
constexpr char kIppMediaLeftMargin[] = "media-left-margin";
constexpr char kIppMediaRightMargin[] = "media-right-margin";
constexpr char kIppMediaSize[] = "media-size";
constexpr char kIppMediaSource[] = "media-source";
constexpr char kIppMediaTopMargin[] = "media-top-margin";
constexpr char kIppXDimension[] = "x-dimension";
constexpr char kIppYDimension[] = "y-dimension";
#if BUILDFLAG(IS_CHROMEOS)
constexpr char kIppDocumentAttributes[] =
@ -44,6 +59,7 @@ constexpr char kPinEncryptionNone[] = "none";
constexpr char kOptionFalse[] = "false";
constexpr char kOptionTrue[] = "true";
// client-info
constexpr char kIppClientInfo[] = "client-info";
constexpr char kIppClientName[] = "client-name";
constexpr char kIppClientPatches[] = "client-patches";

@ -19,13 +19,18 @@ COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppLastDocument[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPin[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPinEncryption[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPrinterUri[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppRequestedAttributes[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppRequestingUserName[];
// printer attributes
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaColDatabase[];
// job attributes
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppCollate[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppCopies[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppColor[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMedia[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaCol[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppDuplex[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppResolution[];
@ -33,6 +38,16 @@ COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppResolution[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCollated[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kUncollated[];
// media-col collection members
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaBottomMargin[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaLeftMargin[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaRightMargin[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaSize[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaSource[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaTopMargin[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppXDimension[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppYDimension[];
#if BUILDFLAG(IS_CHROMEOS)
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppDocumentAttributes[];
@ -43,6 +58,7 @@ COMPONENT_EXPORT(PRINT_BACKEND) extern const char kPinEncryptionNone[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kOptionFalse[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kOptionTrue[];
// client-info
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppClientInfo[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppClientName[];
COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppClientPatches[];

@ -12,7 +12,9 @@
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_set.h"
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/numerics/clamped_math.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
@ -244,23 +246,112 @@ void ExtractResolutions(const CupsOptionProvider& printer,
}
}
absl::optional<PrinterSemanticCapsAndDefaults::Paper>
PaperFromMediaColDatabaseEntry(ipp_t* db_entry) {
DCHECK(db_entry);
ipp_t* media_size = ippGetCollection(
ippFindAttribute(db_entry, kIppMediaSize, IPP_TAG_BEGIN_COLLECTION), 0);
ipp_attribute_t* width_attr =
ippFindAttribute(media_size, kIppXDimension, IPP_TAG_INTEGER);
ipp_attribute_t* height_attr =
ippFindAttribute(media_size, kIppYDimension, IPP_TAG_INTEGER);
if (!width_attr || !height_attr) {
// If x-dimension and y-dimension don't have IPP_TAG_INTEGER, they are
// custom size ranges, so we want to skip this "size".
return absl::nullopt;
}
int width = ippGetInteger(width_attr, 0);
int height = ippGetInteger(height_attr, 0);
ipp_attribute_t* bottom_attr =
ippFindAttribute(db_entry, kIppMediaBottomMargin, IPP_TAG_INTEGER);
ipp_attribute_t* left_attr =
ippFindAttribute(db_entry, kIppMediaLeftMargin, IPP_TAG_INTEGER);
ipp_attribute_t* right_attr =
ippFindAttribute(db_entry, kIppMediaRightMargin, IPP_TAG_INTEGER);
ipp_attribute_t* top_attr =
ippFindAttribute(db_entry, kIppMediaTopMargin, IPP_TAG_INTEGER);
DCHECK(bottom_attr);
DCHECK(left_attr);
DCHECK(right_attr);
DCHECK(top_attr);
int bottom_margin = ippGetInteger(bottom_attr, 0);
int left_margin = ippGetInteger(left_attr, 0);
int right_margin = ippGetInteger(right_attr, 0);
int top_margin = ippGetInteger(top_attr, 0);
if (width <= 0 || height <= 0 || bottom_margin < 0 || top_margin < 0 ||
left_margin < 0 || right_margin < 0 ||
width <= base::ClampedNumeric<int>(left_margin) + right_margin ||
height <= base::ClampedNumeric<int>(bottom_margin) + top_margin) {
LOG(WARNING) << "Invalid media-col-database entry:"
<< " x-dimension=" << width << " y-dimension=" << height
<< " media-bottom-margin=" << bottom_margin
<< " media-left-margin=" << left_margin
<< " media-right-margin=" << right_margin
<< " media-top-margin=" << top_margin;
return absl::nullopt;
}
PrinterSemanticCapsAndDefaults::Paper paper;
paper.size_um =
gfx::Size(width * kMicronsPerPwgUnit, height * kMicronsPerPwgUnit);
paper.printable_area_um = PrintableAreaFromSizeAndPwgMargins(
paper.size_um, bottom_margin, left_margin, right_margin, top_margin);
return paper;
}
bool PaperIsBorderless(const PrinterSemanticCapsAndDefaults::Paper& paper) {
return paper.printable_area_um.x() == 0 && paper.printable_area_um.y() == 0 &&
paper.printable_area_um.width() == paper.size_um.width() &&
paper.printable_area_um.height() == paper.size_um.height();
}
PrinterSemanticCapsAndDefaults::Papers SupportedPapers(
const CupsPrinter& printer) {
std::vector<base::StringPiece> papers =
printer.GetSupportedOptionValueStrings(kIppMedia);
PrinterSemanticCapsAndDefaults::Papers parsed_papers;
parsed_papers.reserve(papers.size());
for (base::StringPiece paper : papers) {
PrinterSemanticCapsAndDefaults::Paper parsed =
ParsePaper(paper, printer.GetMediaMarginsByName(std::string(paper)));
// If a paper fails to parse reasonably, we should avoid propagating
// it - e.g. CUPS is known to give out empty vendor IDs at times:
// https://crbug.com/920295#c23
if (!parsed.display_name.empty()) {
parsed_papers.push_back(parsed);
auto size_compare = [](const gfx::Size& a, const gfx::Size& b) {
auto result = a.width() - b.width();
if (result == 0) {
result = a.height() - b.height();
}
return result < 0;
};
std::map<gfx::Size, PrinterSemanticCapsAndDefaults::Paper,
decltype(size_compare)>
paper_map;
ipp_attribute_t* attr = printer.GetMediaColDatabase();
int count = ippGetCount(attr);
for (int i = 0; i < count; i++) {
ipp_t* db_entry = ippGetCollection(attr, i);
absl::optional<PrinterSemanticCapsAndDefaults::Paper> paper_opt =
PaperFromMediaColDatabaseEntry(db_entry);
if (!paper_opt.has_value()) {
continue;
}
const auto& paper = paper_opt.value();
if (auto existing_entry = paper_map.find(paper.size_um);
existing_entry != paper_map.end()) {
// Prefer non-borderless versions of paper sizes.
if (PaperIsBorderless(existing_entry->second)) {
existing_entry->second = paper;
}
} else {
paper_map.emplace(paper.size_um, paper);
}
}
PrinterSemanticCapsAndDefaults::Papers parsed_papers;
for (const auto& entry : paper_map) {
parsed_papers.push_back(entry.second);
}
return parsed_papers;
}
@ -340,10 +431,6 @@ size_t AddAttributes(const CupsOptionProvider& printer,
size_t AddInputTray(const CupsOptionProvider& printer,
AdvancedCapabilities* caps) {
size_t previous_size = caps->size();
// b/151324273: CUPS doesn't implement media-source in media-col-database like
// it should according to the IPP specs. However, it does implement a naked
// media-source attribute which we can use until the proper changes can be
// made to media-col-database.
KeywordHandler(printer, "media-source", caps);
return caps->size() - previous_size;
}
@ -361,15 +448,17 @@ void ExtractAdvancedCapabilities(const CupsOptionProvider& printer,
} // namespace
PrinterSemanticCapsAndDefaults::Paper DefaultPaper(const CupsPrinter& printer) {
ipp_attribute_t* attr = printer.GetDefaultOptionValue(kIppMedia);
ipp_attribute_t* attr = printer.GetDefaultOptionValue(kIppMediaCol);
if (!attr)
return PrinterSemanticCapsAndDefaults::Paper();
const char* const media_name = ippGetString(attr, 0, nullptr);
if (!media_name) {
ipp_t* media_col_default = ippGetCollection(attr, 0);
if (!media_col_default) {
return PrinterSemanticCapsAndDefaults::Paper();
}
return ParsePaper(media_name, printer.GetMediaMarginsByName(media_name));
PrinterSemanticCapsAndDefaults::Paper paper;
return PaperFromMediaColDatabaseEntry(media_col_default)
.value_or(PrinterSemanticCapsAndDefaults::Paper());
}
void CapsAndDefaultsFromPrinter(const CupsPrinter& printer,
@ -393,6 +482,37 @@ void CapsAndDefaultsFromPrinter(const CupsPrinter& printer,
ExtractResolutions(printer, printer_info);
}
gfx::Rect GetPrintableAreaForSize(const CupsPrinter& printer,
const gfx::Size& size_um) {
ipp_attribute_t* attr = printer.GetMediaColDatabase();
int count = ippGetCount(attr);
gfx::Rect result(0, 0, size_um.width(), size_um.height());
for (int i = 0; i < count; i++) {
ipp_t* db_entry = ippGetCollection(attr, i);
absl::optional<PrinterSemanticCapsAndDefaults::Paper> paper_opt =
PaperFromMediaColDatabaseEntry(db_entry);
if (!paper_opt.has_value()) {
continue;
}
const auto& paper = paper_opt.value();
if (paper.size_um != size_um) {
continue;
}
result = paper.printable_area_um;
// If this is a borderless size, try to find a non-borderless version.
if (!PaperIsBorderless(paper)) {
return result;
}
}
return result;
}
ScopedIppPtr WrapIpp(ipp_t* ipp) {
return ScopedIppPtr(ipp, &ippDelete);
}

@ -31,6 +31,11 @@ COMPONENT_EXPORT(PRINT_BACKEND)
void CapsAndDefaultsFromPrinter(const CupsPrinter& printer,
PrinterSemanticCapsAndDefaults* printer_info);
// Gets the printer margins for the provided paper size.
COMPONENT_EXPORT(PRINT_BACKEND)
gfx::Rect GetPrintableAreaForSize(const CupsPrinter& printer,
const gfx::Size& size_um);
// Wraps `ipp` in unique_ptr with appropriate deleter
COMPONENT_EXPORT(PRINT_BACKEND) ScopedIppPtr WrapIpp(ipp_t* ipp);

@ -35,13 +35,6 @@ class MockCupsPrinterWithMarginsAndAttributes : public MockCupsPrinter {
MockCupsPrinterWithMarginsAndAttributes() = default;
~MockCupsPrinterWithMarginsAndAttributes() override = default;
// CupsPrinter:
CupsMediaMargins GetMediaMarginsByName(
const std::string& media_id) const override {
const auto margins = margins_.find(media_id);
return margins != margins_.end() ? margins->second : CupsMediaMargins();
}
// CupsOptionProvider:
ipp_attribute_t* GetSupportedOptionValues(
const char* option_name) const override {
@ -77,6 +70,11 @@ class MockCupsPrinterWithMarginsAndAttributes : public MockCupsPrinter {
return attr != default_attributes_.end() ? attr->second : nullptr;
}
// CupsOptionProvider:
ipp_attribute_t* GetMediaColDatabase() const override {
return media_col_database_;
}
// CupsOptionProvider:
bool CheckOptionSupported(const char* name,
const char* value) const override {
@ -84,11 +82,6 @@ class MockCupsPrinterWithMarginsAndAttributes : public MockCupsPrinter {
return false;
}
void SetMediaMarginsByName(base::StringPiece media_id,
const CupsMediaMargins& margins) {
margins_[media_id] = margins;
}
void SetSupportedOptions(base::StringPiece name, ipp_attribute_t* attribute) {
supported_attributes_[name] = attribute;
}
@ -97,10 +90,14 @@ class MockCupsPrinterWithMarginsAndAttributes : public MockCupsPrinter {
default_attributes_[name] = attribute;
}
void SetMediaColDatabase(ipp_attribute_t* attribute) {
media_col_database_ = attribute;
}
private:
std::map<base::StringPiece, ipp_attribute_t*> supported_attributes_;
std::map<base::StringPiece, ipp_attribute_t*> default_attributes_;
std::map<base::StringPiece, CupsMediaMargins> margins_;
ipp_attribute_t* media_col_database_;
};
class PrintBackendCupsIppHelperTest : public ::testing::Test {
@ -145,12 +142,83 @@ ipp_attribute_t* MakeStringCollection(ipp_t* ipp,
strings.size(), nullptr, strings.data());
}
struct media_info {
int width;
int height;
int bottom_margin;
int left_margin;
int right_margin;
int top_margin;
std::map<const char*, const char*> keyword_attrs;
bool is_range;
int width_max;
int height_max;
};
ScopedIppPtr MakeMediaCol(const media_info& info) {
ScopedIppPtr media_col = WrapIpp(ippNew());
ScopedIppPtr media_size = WrapIpp(ippNew());
if (info.is_range) {
ippAddRange(media_size.get(), IPP_TAG_ZERO, "x-dimension", info.width,
info.width_max);
ippAddRange(media_size.get(), IPP_TAG_ZERO, "y-dimension", info.height,
info.height_max);
} else {
ippAddInteger(media_size.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
"x-dimension", info.width);
ippAddInteger(media_size.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
"y-dimension", info.height);
}
ippAddCollection(media_col.get(), IPP_TAG_ZERO, "media-size",
media_size.get());
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
"media-bottom-margin", info.bottom_margin);
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
"media-left-margin", info.left_margin);
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
"media-right-margin", info.right_margin);
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
"media-top-margin", info.top_margin);
for (auto& it : info.keyword_attrs) {
ippAddString(media_col.get(), IPP_TAG_ZERO, IPP_TAG_KEYWORD, it.first,
nullptr, it.second);
}
return media_col;
}
ipp_attribute_t* MakeMediaColDefault(ipp_t* ipp, const media_info& info) {
ScopedIppPtr media_col = MakeMediaCol(info);
return ippAddCollection(ipp, IPP_TAG_ZERO, "TEST_DATA", media_col.get());
}
ipp_attribute_t* MakeMediaColDatabase(ipp_t* ipp,
const std::vector<media_info>& media) {
std::vector<ScopedIppPtr> collections;
std::vector<const ipp_t*> raw_collections;
for (auto info : media) {
ScopedIppPtr entry = MakeMediaCol(info);
raw_collections.emplace_back(entry.get());
collections.emplace_back(std::move(entry));
}
return ippAddCollections(ipp, IPP_TAG_PRINTER, "TEST_DATA",
raw_collections.size(), raw_collections.data());
}
TEST_F(PrintBackendCupsIppHelperTest, DefaultPaper) {
const CupsPrinter::CupsMediaMargins kMargins = {10, 10, 10, 10};
EXPECT_EQ(ParsePaper("", kMargins), DefaultPaper(*printer_));
printer_->SetOptionDefault("media", MakeString(ipp_, "iso_a4_210x297mm"));
printer_->SetMediaMarginsByName("iso_a4_210x297mm", kMargins);
EXPECT_EQ(ParsePaper("iso_a4_210x297mm", kMargins), DefaultPaper(*printer_));
EXPECT_EQ(PrinterSemanticCapsAndDefaults::Paper(), DefaultPaper(*printer_));
printer_->SetOptionDefault(
"media-col",
MakeMediaColDefault(ipp_, {21000, 29700, 10, 10, 10, 10, {}}));
PrinterSemanticCapsAndDefaults::Paper default_paper = DefaultPaper(*printer_);
EXPECT_EQ(default_paper.size_um.width(), 210000);
EXPECT_EQ(default_paper.size_um.height(), 297000);
}
TEST_F(PrintBackendCupsIppHelperTest, CopiesCapable) {
@ -224,42 +292,47 @@ TEST_F(PrintBackendCupsIppHelperTest, DuplexNotSupported) {
}
TEST_F(PrintBackendCupsIppHelperTest, A4PaperSupported) {
printer_->SetSupportedOptions(
"media", MakeStringCollection(ipp_, {"iso_a4_210x297mm"}));
printer_->SetMediaColDatabase(
MakeMediaColDatabase(ipp_, {{21000, 29700, 10, 10, 10, 10, {}}}));
PrinterSemanticCapsAndDefaults caps;
CapsAndDefaultsFromPrinter(*printer_, &caps);
PrinterSemanticCapsAndDefaults::Paper paper = caps.papers[0];
// media display name localization is handled more fully in
// AssemblePrinterSettings().
EXPECT_EQ("iso a4", paper.display_name);
EXPECT_EQ("iso_a4_210x297mm", paper.vendor_id);
EXPECT_EQ(210000, paper.size_um.width());
EXPECT_EQ(297000, paper.size_um.height());
}
TEST_F(PrintBackendCupsIppHelperTest, LegalPaperDefault) {
printer_->SetOptionDefault("media", MakeString(ipp_, "na_legal_8.5x14in"));
// na_legal_8.5x14in
printer_->SetOptionDefault(
"media-col",
MakeMediaColDefault(ipp_, {21590, 35560, 10, 10, 10, 10, {}}));
PrinterSemanticCapsAndDefaults caps;
CapsAndDefaultsFromPrinter(*printer_, &caps);
// media display name localization is handled more fully in
// AssemblePrinterSettings().
EXPECT_EQ("na legal", caps.default_paper.display_name);
EXPECT_EQ("na_legal_8.5x14in", caps.default_paper.vendor_id);
EXPECT_EQ(215900, caps.default_paper.size_um.width());
EXPECT_EQ(355600, caps.default_paper.size_um.height());
}
// Tests that CapsAndDefaultsFromPrinter() does not propagate papers
// with badly formatted vendor IDs - such papers will not transform into
// meaningful ParsedPaper instances and are sometimes inimical to
// ARC++.
TEST_F(PrintBackendCupsIppHelperTest, OmitPapersWithoutVendorIds) {
printer_->SetSupportedOptions(
"media", MakeStringCollection(ipp_, {"jis_b5_182x257mm", "invalidsize",
"", "iso_b5_176x250mm"}));
// Tests that CapsAndDefaultsFromPrinter() does not propagate papers with
// invalid sizes or margins to the Chromium print backend.
TEST_F(PrintBackendCupsIppHelperTest, OmitPapersWithInvalidSizes) {
printer_->SetMediaColDatabase(
MakeMediaColDatabase(ipp_, {
{18200, 25700, 100, 100, 100, 100, {}},
{0, 29700, 100, 100, 100, 100, {}},
{-1, 29700, 100, 100, 100, 100, {}},
{21000, 0, 100, 100, 100, 100, {}},
{21000, -1, 100, 100, 100, 100, {}},
{21000, 29700, -1, 100, 100, 100, {}},
{21000, 29700, 100, -1, 100, 100, {}},
{21000, 29700, 100, 100, -1, 100, {}},
{21000, 29700, 100, 100, 100, -1, {}},
{21000, 29700, 100, 10500, 10500, 100, {}},
{21000, 29700, 14850, 100, 100, 14850, {}},
{17600, 25000, 100, 100, 100, 100, {}},
}));
PrinterSemanticCapsAndDefaults caps;
CapsAndDefaultsFromPrinter(*printer_, &caps);
@ -269,54 +342,179 @@ TEST_F(PrintBackendCupsIppHelperTest, OmitPapersWithoutVendorIds) {
// preceding call to CapsAndDefaultsFromPrinter() will have dropped
// these invalid sizes.
ASSERT_EQ(2U, caps.papers.size());
// While not directly pertinent to this test, we expect a certain
// format for the other supported papers.
EXPECT_THAT(
caps.papers,
testing::UnorderedElementsAre(
testing::Field(&PrinterSemanticCapsAndDefaults::Paper::display_name,
"jis b5"),
testing::Field(&PrinterSemanticCapsAndDefaults::Paper::display_name,
"iso b5")));
for (const auto& paper : caps.papers) {
EXPECT_NE(21000, paper.size_um.width());
EXPECT_NE(29700, paper.size_um.height());
}
}
// Tests that CapsAndDefaultsFromPrinter() does not propagate the
// special IPP values that CUPS happens to expose to the Chromium print
// backend.
TEST_F(PrintBackendCupsIppHelperTest, OmitPapersWithSpecialVendorIds) {
// Maintainer's note: there's no reason why a printer would deliver
// two discrete sizes for custom_min* and custom_max*; in practice,
// we always see the fully qualified custom_m(in|ax)_<DIMENSIONS>
// delivered to the Chromium print backend.
printer_->SetSupportedOptions(
"media",
MakeStringCollection(
ipp_, {"na_number-11_4.5x10.375in", "custom_max", "custom_min_0x0in",
"na_govt-letter_8x10in", "custom_min",
"custom_max_1000x1000in", "iso_b0_1000x1414mm"}));
// Tests that CapsAndDefaultsFromPrinter() does not propagate custom size ranges
// from the media-col-database to the Chromium print backend.
TEST_F(PrintBackendCupsIppHelperTest, OmitPapersWithSizeRanges) {
printer_->SetMediaColDatabase(MakeMediaColDatabase(
ipp_, {
{11430, 26352, 100, 100, 100, 100, {}},
{0, 0, 100, 100, 100, 100, {}, true, 2540000, 2540000},
{20320, 25400, 100, 100, 100, 100, {}},
{100000, 141400, 100, 100, 100, 100, {}},
}));
PrinterSemanticCapsAndDefaults caps;
CapsAndDefaultsFromPrinter(*printer_, &caps);
// The printer reports that it supports seven media sizes, four of
// which are not meant for users' eyes (``custom_min*'' and
// ``custom_max*''). The preceding call to
// CapsAndDefaultsFromPrinter() will have dropped these sizes,
// refusing to propagate them out of the backend.
// The printer reports that it supports four media sizes, one of which is not
// meant for users' eyes (the size range). The preceding call to
// CapsAndDefaultsFromPrinter() will have dropped these sizes, refusing to
// propagate them out of the backend.
ASSERT_EQ(3U, caps.papers.size());
}
// While not directly pertinent to this test, we expect a certain
// format for the other supported papers.
EXPECT_THAT(
caps.papers,
testing::UnorderedElementsAre(
testing::Field(&PrinterSemanticCapsAndDefaults::Paper::display_name,
"na number-11"),
testing::Field(&PrinterSemanticCapsAndDefaults::Paper::display_name,
"na govt-letter"),
testing::Field(&PrinterSemanticCapsAndDefaults::Paper::display_name,
"iso b0")));
// Tests that when the media-col-database contains both bordered and borderless
// versions of a size, CapsAndDefaultsFromPrinter() takes the bordered version
// and drops the borderless version.
TEST_F(PrintBackendCupsIppHelperTest, PreferBorderedSizes) {
PrinterSemanticCapsAndDefaults caps;
printer_->SetMediaColDatabase(
MakeMediaColDatabase(ipp_, {
{21000, 29700, 100, 100, 100, 100, {}},
{21000, 29700, 0, 0, 0, 0, {}},
}));
CapsAndDefaultsFromPrinter(*printer_, &caps);
ASSERT_EQ(1U, caps.papers.size());
EXPECT_NE(gfx::Rect(0, 0, 210000, 297000), caps.papers[0].printable_area_um);
printer_->SetMediaColDatabase(
MakeMediaColDatabase(ipp_, {
{21000, 29700, 0, 0, 0, 0, {}},
{21000, 29700, 100, 100, 100, 100, {}},
}));
CapsAndDefaultsFromPrinter(*printer_, &caps);
ASSERT_EQ(1U, caps.papers.size());
EXPECT_NE(gfx::Rect(0, 0, 210000, 297000), caps.papers[0].printable_area_um);
// If the only available version of a size is borderless, go ahead and use it.
// Not sure if any actual printers do this, but it's allowed by the IPP spec.
printer_->SetMediaColDatabase(
MakeMediaColDatabase(ipp_, {
{21000, 29700, 0, 0, 0, 0, {}},
}));
CapsAndDefaultsFromPrinter(*printer_, &caps);
ASSERT_EQ(1U, caps.papers.size());
EXPECT_EQ(gfx::Rect(0, 0, 210000, 297000), caps.papers[0].printable_area_um);
}
// At the time of this writing, there are no media-source or media-type
// attributes in the media-col-database that cupsd gives us. However, according
// to the IPP spec, each paper size *should* have a separate variant for each
// supported combination of size and type. So make sure behavior doesn't change
// and we don't create duplicate paper sizes when/if CUPS improves in the
// future.
TEST_F(PrintBackendCupsIppHelperTest, NoDuplicateSizes) {
printer_->SetMediaColDatabase(MakeMediaColDatabase(
ipp_,
{
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "stationery"}, {"media-source", "main"}}},
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "stationery"}, {"media-source", "main"}}},
{21000,
29700,
500,
500,
500,
500,
{{"media-type", "stationery"}, {"media-source", "main"}}},
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "photographic"}, {"media-source", "main"}}},
{21000,
29700,
0,
0,
0,
0,
{{"media-type", "photographic"}, {"media-source", "main"}}},
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "photographic-high-gloss"},
{"media-source", "main"}}},
{21000,
29700,
0,
0,
0,
0,
{{"media-type", "photographic-high-gloss"},
{"media-source", "main"}}},
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "photographic-glossy"}, {"media-source", "main"}}},
{21000,
29700,
0,
0,
0,
0,
{{"media-type", "photographic-glossy"}, {"media-source", "main"}}},
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "photographic-semi-gloss"},
{"media-source", "main"}}},
{21000,
29700,
0,
0,
0,
0,
{{"media-type", "photographic-semi-gloss"},
{"media-source", "main"}}},
{21000,
29700,
300,
300,
300,
300,
{{"media-type", "photographic-matte"}, {"media-source", "main"}}},
{21000,
29700,
0,
0,
0,
0,
{{"media-type", "photographic-matte"}, {"media-source", "main"}}},
}));
PrinterSemanticCapsAndDefaults caps;
CapsAndDefaultsFromPrinter(*printer_, &caps);
ASSERT_EQ(1U, caps.papers.size());
}
#if BUILDFLAG(IS_CHROMEOS)

@ -26,7 +26,9 @@ namespace printing {
class CupsPrinterImpl : public CupsPrinter {
public:
CupsPrinterImpl(http_t* http, ScopedDestination dest)
: cups_http_(http), destination_(std::move(dest)) {
: cups_http_(http),
destination_(std::move(dest)),
printer_attributes_(WrapIpp(nullptr)) {
DCHECK(cups_http_);
DCHECK(destination_);
@ -117,6 +119,16 @@ class CupsPrinterImpl : public CupsPrinter {
return supported == 1;
}
// CupsOptionProvider
ipp_attribute_t* GetMediaColDatabase() const override {
if (!EnsurePrinterAttributes()) {
return nullptr;
}
return ippFindAttribute(printer_attributes_.get(), kIppMediaColDatabase,
IPP_TAG_BEGIN_COLLECTION);
}
bool ToPrinterInfo(PrinterBasicInfo* printer_info) const override {
const cups_dest_t* printer = destination_.get();
@ -275,22 +287,37 @@ class CupsPrinterImpl : public CupsPrinter {
return status == IPP_STATUS_OK;
}
CupsMediaMargins GetMediaMarginsByName(
const std::string& media_id) const override {
cups_size_t cups_media;
if (!EnsureDestInfo() ||
!cupsGetDestMediaByName(cups_http_, destination_.get(),
dest_info_.get(), media_id.c_str(),
CUPS_MEDIA_FLAGS_DEFAULT, &cups_media)) {
return {0, 0, 0, 0};
private:
// Sends the request to populate `printer_attributes_` if it's not already
// populated.
bool EnsurePrinterAttributes() const {
if (printer_attributes_) {
return true;
}
return {cups_media.bottom, cups_media.left, cups_media.right,
cups_media.top};
ScopedIppPtr request = CreateRequest(IPP_OP_GET_PRINTER_ATTRIBUTES, "");
// The requested attributes can be changed to "all","media-col-database" if
// we want to directly query printer attributes other than
// media-col-database in the future.
constexpr const char* kRequestedAttributes[] = {kIppMediaColDatabase};
ippAddStrings(request.get(), IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
kIppRequestedAttributes, std::size(kRequestedAttributes),
nullptr, kRequestedAttributes);
// cupsDoRequest() takes ownership of the request and frees it for us.
printer_attributes_.reset(
cupsDoRequest(cups_http_, request.release(), resource_path_.c_str()));
if (ippGetStatusCode(printer_attributes_.get()) != IPP_STATUS_OK) {
printer_attributes_.reset();
return false;
}
return true;
}
private:
// internal helper function to initialize an IPP request
ScopedIppPtr CreateRequest(ipp_op_t op, const std::string& username) {
ScopedIppPtr CreateRequest(ipp_op_t op, const std::string& username) const {
const char* c_username = username.empty() ? cupsUser() : username.c_str();
ipp_t* request = ippNewRequest(op);
@ -303,7 +330,9 @@ class CupsPrinterImpl : public CupsPrinter {
}
// internal helper function to copy attributes to an IPP request
void CopyAttributeGroup(ipp_t* request, ipp_t* attributes, ipp_tag_t group) {
void CopyAttributeGroup(ipp_t* request,
ipp_t* attributes,
ipp_tag_t group) const {
for (ipp_attribute_t* attr = ippFirstAttribute(attributes); attr;
attr = ippNextAttribute(attributes)) {
if (ippGetGroupTag(attr) == group) {
@ -326,6 +355,9 @@ class CupsPrinterImpl : public CupsPrinter {
// resource path used to connect to this printer
std::string resource_path_;
// printer attributes that describe the supported options
mutable ScopedIppPtr printer_attributes_;
};
std::unique_ptr<CupsPrinter> CupsPrinter::Create(http_t* http,

@ -42,6 +42,10 @@ class COMPONENT_EXPORT(PRINT_BACKEND) CupsOptionProvider {
// Returns true if the `value` is supported by option `name`.
virtual bool CheckOptionSupported(const char* name,
const char* value) const = 0;
// Returns the IPP "media-col-database" attribute for this printer.
// ipp_attribute_t* is owned by CupsOptionProvider.
virtual ipp_attribute_t* GetMediaColDatabase() const = 0;
};
// Represents a CUPS printer.
@ -50,18 +54,6 @@ class COMPONENT_EXPORT(PRINT_BACKEND) CupsOptionProvider {
// share an http connection which the CupsConnection closes on destruction.
class COMPONENT_EXPORT(PRINT_BACKEND) CupsPrinter : public CupsOptionProvider {
public:
// Represents the margins that CUPS reports for some given media.
// Its members are valued in PWG units (100ths of mm).
// This struct approximates a cups_size_t, which is BLRT.
// `bottom`, `left`, `right`, and `top` express inward encroachment by
// margins, away from the edges of the paper.
struct CupsMediaMargins {
int bottom;
int left;
int right;
int top;
};
~CupsPrinter() override = default;
// Create a printer with a connection defined by `http` and `dest`.
@ -124,16 +116,6 @@ class COMPONENT_EXPORT(PRINT_BACKEND) CupsPrinter : public CupsOptionProvider {
// Cancel the print job `job_id`. Returns true if the operation succeeded.
// Returns false if it failed for any reason.
virtual bool CancelJob(int job_id) = 0;
// Queries CUPS for the margins of the media named by `media_id`.
//
// A `media_id` is any vendor ID known to CUPS for a given printer.
// Vendor IDs are exemplified by the keys of the big map in
// print_media_l10n.cc.
//
// Returns all zeroes if the CUPS API call fails.
virtual CupsMediaMargins GetMediaMarginsByName(
const std::string& media_id) const = 0;
};
} // namespace printing

@ -38,13 +38,12 @@ class MockCupsPrinter : public CupsPrinter {
MOCK_METHOD0(FinishDocument, bool());
MOCK_METHOD2(CloseJob, ipp_status_t(int job_id, const std::string& username));
MOCK_METHOD1(CancelJob, bool(int job_id));
MOCK_CONST_METHOD1(GetMediaMarginsByName,
CupsMediaMargins(const std::string& media_id));
MOCK_CONST_METHOD1(GetSupportedOptionValues,
ipp_attribute_t*(const char* option_name));
MOCK_CONST_METHOD1(GetSupportedOptionValueStrings,
std::vector<base::StringPiece>(const char* option_name));
MOCK_CONST_METHOD0(GetMediaColDatabase, ipp_attribute_t*());
MOCK_CONST_METHOD1(GetDefaultOptionValue,
ipp_attribute_t*(const char* option_name));
MOCK_CONST_METHOD2(CheckOptionSupported,

@ -106,52 +106,49 @@ gfx::Size ParsePaperSize(base::StringPiece value) {
}
#if BUILDFLAG(USE_CUPS)
PrinterSemanticCapsAndDefaults::Paper ParsePaper(
base::StringPiece value,
const CupsPrinter::CupsMediaMargins& margins) {
std::vector<base::StringPiece> pieces = GetStringPiecesIfValid(value);
if (pieces.empty()) {
return PrinterSemanticCapsAndDefaults::Paper();
}
base::StringPiece dimensions = pieces.back();
PrinterSemanticCapsAndDefaults::Paper paper;
paper.vendor_id = std::string(value);
paper.size_um = DimensionsToMicrons(dimensions);
if (paper.size_um.IsEmpty()) {
return PrinterSemanticCapsAndDefaults::Paper();
}
gfx::Rect PrintableAreaFromSizeAndPwgMargins(const gfx::Size& size_um,
int bottom_pwg,
int left_pwg,
int right_pwg,
int top_pwg) {
// The margins of the printable area are expressed in PWG units (100ths of
// mm).
int printable_area_left_um = margins.left * kMicronsPerPwgUnit;
int printable_area_bottom_um = margins.bottom * kMicronsPerPwgUnit;
// mm) in the IPP 'media-col-database' attribute.
int printable_area_left_um = left_pwg * kMicronsPerPwgUnit;
int printable_area_bottom_um = bottom_pwg * kMicronsPerPwgUnit;
int printable_area_width_um =
paper.size_um.width() -
((margins.left + margins.right) * kMicronsPerPwgUnit);
int printable_area_length_um =
paper.size_um.height() -
((margins.top + margins.bottom) * kMicronsPerPwgUnit);
paper.printable_area_um =
gfx::Rect(printable_area_left_um, printable_area_bottom_um,
printable_area_width_um, printable_area_length_um);
size_um.width() - ((left_pwg + right_pwg) * kMicronsPerPwgUnit);
int printable_area_height_um =
size_um.height() - ((top_pwg + bottom_pwg) * kMicronsPerPwgUnit);
return gfx::Rect(printable_area_left_um, printable_area_bottom_um,
printable_area_width_um, printable_area_height_um);
}
// Default to the paper size if printable area is empty.
// We've seen some drivers have a printable area that goes out of bounds
// of the paper size. In those cases, set the printable area to be the
// size. (See crbug.com/1412305.)
const gfx::Rect size_um_rect = gfx::Rect(paper.size_um);
if (paper.printable_area_um.IsEmpty() ||
!size_um_rect.Contains(paper.printable_area_um)) {
paper.printable_area_um = size_um_rect;
}
void PwgMarginsFromSizeAndPrintableArea(const gfx::Size& size_um,
const gfx::Rect& printable_area_um,
int* bottom_pwg,
int* left_pwg,
int* right_pwg,
int* top_pwg) {
DCHECK(bottom_pwg);
DCHECK(left_pwg);
DCHECK(right_pwg);
DCHECK(top_pwg);
// Omits the final token describing the media dimensions.
pieces.pop_back();
paper.display_name = base::JoinString(pieces, " ");
// These values in microns were obtained in the first place by converting
// from PWG units, so we can losslessly convert them back.
int bottom_um = printable_area_um.y();
int left_um = printable_area_um.x();
int right_um = size_um.width() - printable_area_um.right();
int top_um = size_um.height() - printable_area_um.bottom();
DCHECK_EQ(bottom_um % kMicronsPerPwgUnit, 0);
DCHECK_EQ(left_um % kMicronsPerPwgUnit, 0);
DCHECK_EQ(right_um % kMicronsPerPwgUnit, 0);
DCHECK_EQ(top_um % kMicronsPerPwgUnit, 0);
return paper;
*bottom_pwg = bottom_um / kMicronsPerPwgUnit;
*left_pwg = left_um / kMicronsPerPwgUnit;
*right_pwg = right_um / kMicronsPerPwgUnit;
*top_pwg = top_um / kMicronsPerPwgUnit;
}
#endif // BUILDFLAG(USE_CUPS)

@ -33,18 +33,26 @@ COMPONENT_EXPORT(PRINT_BACKEND)
gfx::Size ParsePaperSize(base::StringPiece value);
#if BUILDFLAG(USE_CUPS)
// Parses the media name expressed by `value` into a Paper. Returns an
// empty Paper if `value` does not contain the display name nor the dimension,
// `value` contains a prefix of media sizes not meant for users' eyes, or if the
// paper size is empty.
// `margins` is used to calculate the Paper's printable area.
// We don't handle l10n here. We do populate the display_name member with the
// prettified vendor ID, but fully expect the caller to clobber this if a better
// localization exists.
// Calculates a paper's printable area in microns from its size in microns and
// its four margins in PWG units.
COMPONENT_EXPORT(PRINT_BACKEND)
PrinterSemanticCapsAndDefaults::Paper ParsePaper(
base::StringPiece value,
const CupsPrinter::CupsMediaMargins& margins);
gfx::Rect PrintableAreaFromSizeAndPwgMargins(const gfx::Size& size_um,
int bottom_pwg,
int left_pwg,
int right_pwg,
int top_pwg);
// Calculates a paper's four margins in PWG units from its size and printable
// area in microns. Since the size and printable area were converted from PWG
// units in the first place, the margins in PWG units can be reconstructed
// losslessly.
COMPONENT_EXPORT(PRINT_BACKEND)
void PwgMarginsFromSizeAndPrintableArea(const gfx::Size& size_um,
const gfx::Rect& printable_area_um,
int* bottom_pwg,
int* left_pwg,
int* right_pwg,
int* top_pwg);
#endif // BUILDFLAG(USE_CUPS)
} // namespace printing

@ -56,93 +56,27 @@ TEST(PrintBackendUtilsTest, ParsePaperSizeBadOneDimension) {
#if BUILDFLAG(USE_CUPS)
TEST(PrintBackendUtilsCupsTest, ParsePaperA4) {
constexpr CupsPrinter::CupsMediaMargins kMargins = {500, 500, 500, 500};
PrinterSemanticCapsAndDefaults::Paper paper =
ParsePaper("iso_a4_210x297mm", kMargins);
EXPECT_EQ(gfx::Size(210000, 297000), paper.size_um);
EXPECT_EQ("iso_a4_210x297mm", paper.vendor_id);
EXPECT_EQ("iso a4", paper.display_name);
EXPECT_EQ(gfx::Rect(5000, 5000, 200000, 287000), paper.printable_area_um);
TEST(PrintBackendUtilsCupsTest, PrintableAreaFromMarginsA4) {
// margins in PWG units (1 PWG unit = 1/100 mm = 10 um)
int bottom = 100;
int left = 200;
int right = 300;
int top = 400;
gfx::Size size_um = {210000, 297000};
gfx::Rect printable_area_um =
PrintableAreaFromSizeAndPwgMargins(size_um, bottom, left, right, top);
EXPECT_EQ(gfx::Rect(2000, 1000, 205000, 292000), printable_area_um);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperNaLetter) {
constexpr CupsPrinter::CupsMediaMargins kMargins = {500, 500, 500, 500};
PrinterSemanticCapsAndDefaults::Paper paper =
ParsePaper("na_letter_8.5x11in", kMargins);
EXPECT_EQ(gfx::Size(215900, 279400), paper.size_um);
EXPECT_EQ("na_letter_8.5x11in", paper.vendor_id);
EXPECT_EQ("na letter", paper.display_name);
EXPECT_EQ(gfx::Rect(5000, 5000, 205900, 269400), paper.printable_area_um);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperNaIndex4x6) {
// Note that "na_index-4x6_4x6in" has a dimension within the media name. Test
// that parsing is not affected.
constexpr CupsPrinter::CupsMediaMargins kMargins = {500, 500, 500, 500};
PrinterSemanticCapsAndDefaults::Paper paper =
ParsePaper("na_index-4x6_4x6in", kMargins);
EXPECT_EQ(gfx::Size(101600, 152400), paper.size_um);
EXPECT_EQ("na_index-4x6_4x6in", paper.vendor_id);
EXPECT_EQ("na index-4x6", paper.display_name);
EXPECT_EQ(gfx::Rect(5000, 5000, 91600, 142400), paper.printable_area_um);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperNaNumber10) {
// Test that a paper size with a fractional dimension is not affected by
// rounding errors.
constexpr CupsPrinter::CupsMediaMargins kMargins = {1000, 1000, 1000, 1000};
PrinterSemanticCapsAndDefaults::Paper paper =
ParsePaper("na_number-10_4.125x9.5in", kMargins);
EXPECT_EQ(gfx::Size(104775, 241300), paper.size_um);
EXPECT_EQ("na_number-10_4.125x9.5in", paper.vendor_id);
EXPECT_EQ("na number-10", paper.display_name);
EXPECT_EQ(gfx::Rect(10000, 10000, 84775, 221300), paper.printable_area_um);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperBadUnit) {
PrinterSemanticCapsAndDefaults::Paper paper_bad =
ParsePaper("bad_unit_666x666bad", CupsPrinter::CupsMediaMargins());
EXPECT_EQ(PrinterSemanticCapsAndDefaults::Paper(), paper_bad);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperBadOneDimension) {
PrinterSemanticCapsAndDefaults::Paper paper_bad =
ParsePaper("bad_one_dimension_666mm", CupsPrinter::CupsMediaMargins());
EXPECT_EQ(PrinterSemanticCapsAndDefaults::Paper(), paper_bad);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperOutOfBoundsMargins) {
// Given invalid margins, the printable area cannot be calculated correctly.
// The printable area should be set to the paper size as default.
constexpr CupsPrinter::CupsMediaMargins kMargins = {100, 100, 300000, 100};
PrinterSemanticCapsAndDefaults::Paper paper =
ParsePaper("iso_a4_210x297mm", kMargins);
EXPECT_EQ(gfx::Size(210000, 297000), paper.size_um);
EXPECT_EQ("iso_a4_210x297mm", paper.vendor_id);
EXPECT_EQ("iso a4", paper.display_name);
EXPECT_EQ(gfx::Rect(0, 0, 210000, 297000), paper.printable_area_um);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperEmptyPrintableArea) {
// If the printable area is empty, the printable area should be set to the
// paper size.
constexpr CupsPrinter::CupsMediaMargins kMargins = {29700, 0, 0, 0};
PrinterSemanticCapsAndDefaults::Paper paper =
ParsePaper("iso_a4_210x297mm", kMargins);
EXPECT_EQ(gfx::Size(210000, 297000), paper.size_um);
EXPECT_EQ("iso_a4_210x297mm", paper.vendor_id);
EXPECT_EQ("iso a4", paper.display_name);
EXPECT_EQ(gfx::Rect(0, 0, 210000, 297000), paper.printable_area_um);
}
TEST(PrintBackendUtilsCupsTest, ParsePaperEmptySizeWithPrintableArea) {
// If the paper size is empty, the Paper should be invalid, even when provided
// a printable area.
constexpr CupsPrinter::CupsMediaMargins kMargins = {1000, 1000, 1000, 1000};
PrinterSemanticCapsAndDefaults::Paper paper_bad =
ParsePaper("bad_unit_666x666bad", kMargins);
EXPECT_EQ(PrinterSemanticCapsAndDefaults::Paper(), paper_bad);
TEST(PrintBackendUtilsCupsTest, MarginsFromPrintableAreaA4) {
int bottom, left, right, top;
PwgMarginsFromSizeAndPrintableArea({210000, 297000},
{2000, 1000, 205000, 292000}, &bottom,
&left, &right, &top);
EXPECT_EQ(100, bottom);
EXPECT_EQ(200, left);
EXPECT_EQ(300, right);
EXPECT_EQ(400, top);
}
#endif // BUILDFLAG(USE_CUPS)

@ -22,6 +22,7 @@
#include "printing/backend/cups_ipp_constants.h"
#include "printing/backend/cups_ipp_helper.h"
#include "printing/backend/cups_printer.h"
#include "printing/backend/print_backend_utils.h"
#include "printing/buildflags/buildflags.h"
#include "printing/client_info_helpers.h"
#include "printing/metafile.h"
@ -96,48 +97,53 @@ void EncodeClientInfo(const std::vector<mojom::IppClientInfo>& client_infos,
raw_option_values.size(), raw_option_values.data());
}
// Construct the IPP media-col attribute specifying media size, margins, source,
// etc., and add it to 'options'.
void EncodeMediaCol(ipp_t* options,
const gfx::Size& size_um,
const gfx::Rect& printable_area_um,
const std::string& source) {
// The size and printable area in microns were calculated from the size and
// margins in PWG units, so we can losslessly convert them back.
DCHECK_EQ(size_um.width() % kMicronsPerPwgUnit, 0);
DCHECK_EQ(size_um.height() % kMicronsPerPwgUnit, 0);
int width = size_um.width() / kMicronsPerPwgUnit;
int height = size_um.height() / kMicronsPerPwgUnit;
int bottom_margin = 0, left_margin = 0, right_margin = 0, top_margin = 0;
PwgMarginsFromSizeAndPrintableArea(size_um, printable_area_um, &bottom_margin,
&left_margin, &right_margin, &top_margin);
ScopedIppPtr media_col = WrapIpp(ippNew());
ScopedIppPtr media_size = WrapIpp(ippNew());
ippAddInteger(media_size.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER, kIppXDimension,
width);
ippAddInteger(media_size.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER, kIppYDimension,
height);
ippAddCollection(media_col.get(), IPP_TAG_ZERO, kIppMediaSize,
media_size.get());
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
kIppMediaBottomMargin, bottom_margin);
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
kIppMediaLeftMargin, left_margin);
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
kIppMediaRightMargin, right_margin);
ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
kIppMediaTopMargin, top_margin);
if (!source.empty()) {
ippAddString(media_col.get(), IPP_TAG_ZERO, IPP_TAG_KEYWORD,
kIppMediaSource, nullptr, source.c_str());
}
ippAddCollection(options, IPP_TAG_JOB, kIppMediaCol, media_col.get());
}
std::string GetCollateString(bool collate) {
return collate ? kCollated : kUncollated;
}
// Given an integral `value` expressed in PWG units (1/100 mm), returns
// the same value expressed in device units.
int PwgUnitsToDeviceUnits(int value, float micrometers_per_device_unit) {
return ConvertUnitFloat(value, micrometers_per_device_unit, 10);
}
// Given a `media_size`, the specification of the media's `margins`, and
// the number of micrometers per device unit, returns the rectangle
// bounding the apparent printable area of said media.
gfx::Rect RepresentPrintableArea(const gfx::Size& media_size,
const CupsPrinter::CupsMediaMargins& margins,
float micrometers_per_device_unit) {
// These values express inward encroachment by margins, away from the
// edges of the `media_size`.
int left_bound =
PwgUnitsToDeviceUnits(margins.left, micrometers_per_device_unit);
int bottom_bound =
PwgUnitsToDeviceUnits(margins.bottom, micrometers_per_device_unit);
int right_bound =
PwgUnitsToDeviceUnits(margins.right, micrometers_per_device_unit);
int top_bound =
PwgUnitsToDeviceUnits(margins.top, micrometers_per_device_unit);
// These values express the bounding box of the printable area on the
// page.
int printable_width = media_size.width() - (left_bound + right_bound);
int printable_height = media_size.height() - (top_bound + bottom_bound);
if (printable_width > 0 && printable_height > 0) {
return {left_bound, bottom_bound, printable_width, printable_height};
}
return {0, 0, media_size.width(), media_size.height()};
}
void SetPrintableArea(PrintSettings* settings,
const PrintSettings::RequestedMedia& media,
const CupsPrinter::CupsMediaMargins& margins) {
const gfx::Rect& printable_area_um) {
if (!media.size_microns.IsEmpty()) {
float device_microns_per_device_unit =
static_cast<float>(kMicronsPerInch) / settings->device_units_per_inch();
@ -145,8 +151,11 @@ void SetPrintableArea(PrintSettings* settings,
gfx::Size(media.size_microns.width() / device_microns_per_device_unit,
media.size_microns.height() / device_microns_per_device_unit);
gfx::Rect paper_rect = RepresentPrintableArea(
paper_size, margins, device_microns_per_device_unit);
gfx::Rect paper_rect =
gfx::Rect(printable_area_um.x() / device_microns_per_device_unit,
printable_area_um.y() / device_microns_per_device_unit,
printable_area_um.width() / device_microns_per_device_unit,
printable_area_um.height() / device_microns_per_device_unit);
settings->SetPrinterPrintableArea(paper_size, paper_rect,
/*landscape_needs_flip=*/true);
}
@ -154,7 +163,8 @@ void SetPrintableArea(PrintSettings* settings,
} // namespace
ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings) {
ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings,
const gfx::Rect& printable_area_um) {
ScopedIppPtr scoped_options = WrapIpp(ippNew());
ipp_t* options = scoped_options.get();
@ -179,9 +189,6 @@ ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings) {
// color
ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppColor, nullptr,
GetIppColorModelForModel(settings.color()).c_str());
// paper size
ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppMedia, nullptr,
settings.requested_media().vendor_id.c_str());
// copies
ippAddInteger(options, IPP_TAG_JOB, IPP_TAG_INTEGER, kIppCopies,
settings.copies());
@ -203,11 +210,17 @@ ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings) {
}
std::map<std::string, std::vector<int>> multival;
std::string media_source;
for (const auto& setting : settings.advanced_settings()) {
const std::string& key = setting.first;
const std::string& value = setting.second.GetString();
if (value.empty())
if (value.empty()) {
continue;
}
if (key == kIppMediaSource) {
media_source = value;
continue;
}
// Check for multivalue enum ("attribute/value").
size_t pos = key.find('/');
@ -227,6 +240,11 @@ ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings) {
}
}
// Construct the IPP media-col attribute specifying media size, margins,
// source, etc.
EncodeMediaCol(options, settings.requested_media().size_microns,
printable_area_um, media_source);
// Add multivalue enum options.
for (const auto& it : multival) {
ippAddIntegers(options, IPP_TAG_JOB, IPP_TAG_ENUM, it.first.c_str(),
@ -325,10 +343,7 @@ mojom::ResultCode PrintingContextChromeos::UseDefaultSettings() {
media.vendor_id = paper.vendor_id;
media.size_microns = paper.size_um;
settings_->set_requested_media(media);
CupsPrinter::CupsMediaMargins margins =
printer_->GetMediaMarginsByName(paper.vendor_id);
SetPrintableArea(settings_.get(), media, margins);
SetPrintableArea(settings_.get(), media, paper.printable_area_um);
return mojom::ResultCode::kSuccess;
}
@ -387,10 +402,10 @@ mojom::ResultCode PrintingContextChromeos::UpdatePrinterSettings(
settings_->set_requested_media(media);
}
CupsPrinter::CupsMediaMargins margins =
printer_->GetMediaMarginsByName(media.vendor_id);
SetPrintableArea(settings_.get(), media, margins);
ipp_options_ = SettingsToIPPOptions(*settings_);
gfx::Rect printable_area_um =
GetPrintableAreaForSize(*printer_, media.size_microns);
SetPrintableArea(settings_.get(), media, printable_area_um);
ipp_options_ = SettingsToIPPOptions(*settings_, printable_area_um);
send_user_info_ = settings_->send_user_info();
if (send_user_info_) {
DCHECK(printer_);

@ -67,7 +67,8 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextChromeos
// This has the side effect of recording UMA for advanced attributes usage,
// so only call once per job.
COMPONENT_EXPORT(PRINTING)
ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings);
ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings,
const gfx::Rect& printable_area_um);
} // namespace printing

@ -35,6 +35,9 @@ constexpr char kUsername[] = "test user";
constexpr char kDocumentName[] = "document name";
constexpr char16_t kDocumentName16[] = u"document name";
constexpr gfx::Size kDefaultPaperSize = {215900, 279400};
constexpr char kDefaultPaperName[] = "some_vendor_id";
class MockCupsConnection : public CupsConnection {
public:
MOCK_METHOD1(GetDests, bool(std::vector<std::unique_ptr<CupsPrinter>>&));
@ -76,6 +79,7 @@ class PrintingContextTest : public testing::Test,
settings->set_duplex_mode(mojom::DuplexMode::kLongEdge);
settings->set_username(kUsername);
printing_context_->UpdatePrintSettingsFromPOD(std::move(settings));
settings_.set_requested_media({kDefaultPaperSize, kDefaultPaperName});
}
ipp_attribute_t* GetAttribute(ipp_t* attributes,
@ -97,20 +101,20 @@ class PrintingContextTest : public testing::Test,
void TestStringOptionValue(const char* attr_name,
const char* expected_value) const {
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
auto* attr = GetAttribute(attributes.get(), attr_name);
EXPECT_STREQ(expected_value, ippGetString(attr, 0, nullptr));
}
void TestIntegerOptionValue(const char* attr_name, int expected_value) const {
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
auto* attr = GetAttribute(attributes.get(), attr_name);
EXPECT_EQ(expected_value, ippGetInteger(attr, 0));
}
void TestOctetStringOptionValue(const char* attr_name,
base::span<const char> expected_value) const {
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
auto* attr = GetAttribute(attributes.get(), attr_name);
int length;
void* value = ippGetOctetString(attr, 0, &length);
@ -122,7 +126,7 @@ class PrintingContextTest : public testing::Test,
void TestResolutionOptionValue(const char* attr_name,
int expected_x_res,
int expected_y_res) const {
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
auto* attr = GetAttribute(attributes.get(), attr_name);
ipp_res_t unit;
int y_res;
@ -132,18 +136,47 @@ class PrintingContextTest : public testing::Test,
EXPECT_EQ(expected_y_res, y_res);
}
void TestMediaColValue(const gfx::Size& expected_size,
int expected_bottom_margin,
int expected_left_margin,
int expected_right_margin,
int expected_top_margin) {
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
ipp_t* media_col =
ippGetCollection(GetAttribute(attributes.get(), kIppMediaCol), 0);
ipp_t* media_size =
ippGetCollection(GetAttribute(media_col, kIppMediaSize), 0);
int width = ippGetInteger(GetAttribute(media_size, kIppXDimension), 0);
int height = ippGetInteger(GetAttribute(media_size, kIppYDimension), 0);
EXPECT_EQ(expected_size.width(), width);
EXPECT_EQ(expected_size.height(), height);
int bottom =
ippGetInteger(GetAttribute(media_col, kIppMediaBottomMargin), 0);
int left = ippGetInteger(GetAttribute(media_col, kIppMediaLeftMargin), 0);
int right = ippGetInteger(GetAttribute(media_col, kIppMediaRightMargin), 0);
int top = ippGetInteger(GetAttribute(media_col, kIppMediaTopMargin), 0);
EXPECT_EQ(expected_bottom_margin, bottom);
EXPECT_EQ(expected_left_margin, left);
EXPECT_EQ(expected_right_margin, right);
EXPECT_EQ(expected_top_margin, top);
}
bool HasAttribute(const char* attr_name) const {
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
return !!ippFindAttribute(attributes.get(), attr_name, IPP_TAG_ZERO);
}
int GetAttrValueCount(const char* attr_name) const {
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
auto* attr = GetAttribute(attributes.get(), attr_name);
return ippGetCount(attr);
}
TestPrintSettings settings_;
gfx::Rect printable_area_;
// PrintingContext::Delegate methods.
gfx::NativeView GetParentView() override { return nullptr; }
@ -169,11 +202,12 @@ TEST_F(PrintingContextTest, SettingsToIPPOptions_Duplex) {
TestStringOptionValue(kIppDuplex, "two-sided-short-edge");
}
TEST_F(PrintingContextTest, SettingsToIPPOptions_Media) {
TestStringOptionValue(kIppMedia, "");
TEST_F(PrintingContextTest, SettingsToIPPOptions_MediaCol) {
settings_.set_requested_media(
{gfx::Size(297000, 420000), "iso_a3_297x420mm"});
TestStringOptionValue(kIppMedia, "iso_a3_297x420mm");
printable_area_ =
gfx::Rect(2000, 1000, 297000 - (2000 + 3000), 420000 - (1000 + 4000));
TestMediaColValue(gfx::Size(29700, 42000), 100, 200, 300, 400);
}
TEST_F(PrintingContextTest, SettingsToIPPOptions_Copies) {
@ -285,7 +319,7 @@ TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfo) {
"a.1-B_");
settings_.set_client_infos({client_info});
auto attributes = SettingsToIPPOptions(settings_);
auto attributes = SettingsToIPPOptions(settings_, printable_area_);
auto* attr = ippFindAttribute(attributes.get(), kIppClientInfo,
IPP_TAG_BEGIN_COLLECTION);
auto* client_info_collection = ippGetCollection(attr, 0);