From d8f4bc770c64d26691a5846d555dc34610901aab Mon Sep 17 00:00:00 2001 From: Daniel Hosseinian Date: Mon, 16 Dec 2019 20:13:17 +0000 Subject: [PATCH] Select default paper size based on locale if default isn't set in PPD Currently, when the default page size of a printer cannot be parsed from the PPD, the default falls back to the first page size on the list of capabilities. Because such a page size tends to be the first alphanumerically, it tends to be one that is not commonly printed on, such as A2, A3, or 11x17. Using the locale information allows the parser to fall back on either A4 or N.A. Letter, respecting the locale. In doing so, alter the signature of ParsePpdCapabilities() to read the locale, which is then passed to GetDefaultPaperSizeFromLocaleMicrons(). Change some instances of const to constexpr along the way. Bug: 890904 Change-Id: I4ecd824008a7045086919630e491699d05bd78b1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1937705 Commit-Queue: Daniel Hosseinian Reviewed-by: Lei Zhang Cr-Commit-Position: refs/heads/master@{#725235} --- printing/backend/cups_helper.cc | 29 +++++++- printing/backend/cups_helper.h | 1 + printing/backend/cups_helper_unittest.cc | 92 ++++++++++++++++++++---- printing/backend/print_backend_cups.cc | 2 +- printing/printing_utils.cc | 40 ++++++++++- printing/printing_utils.h | 16 +++++ printing/printing_utils_unittest.cc | 54 +++++++++++++- printing/units.h | 22 +++--- 8 files changed, 226 insertions(+), 30 deletions(-) diff --git a/printing/backend/cups_helper.cc b/printing/backend/cups_helper.cc index 978820a9a2f5e4..039d8f09e3f52d 100644 --- a/printing/backend/cups_helper.cc +++ b/printing/backend/cups_helper.cc @@ -20,6 +20,7 @@ #include "base/values.h" #include "printing/backend/print_backend.h" #include "printing/backend/print_backend_consts.h" +#include "printing/printing_utils.h" #include "printing/units.h" #include "url/gurl.h" @@ -426,6 +427,7 @@ http_t* HttpConnectionCUPS::http() { } bool ParsePpdCapabilities(base::StringPiece printer_name, + base::StringPiece locale, base::StringPiece printer_capabilities, PrinterSemanticCapsAndDefaults* printer_info) { base::FilePath ppd_file_path; @@ -474,11 +476,12 @@ bool ParsePpdCapabilities(base::StringPiece printer_name, if (ppd->num_sizes > 0 && ppd->sizes) { VLOG(1) << "Paper list size - " << ppd->num_sizes; ppd_option_t* paper_option = ppdFindOption(ppd, kPageSize); + bool is_default_found = false; for (int i = 0; i < ppd->num_sizes; ++i) { gfx::Size paper_size_microns( ConvertUnit(ppd->sizes[i].width, kPointsPerInch, kMicronsPerInch), ConvertUnit(ppd->sizes[i].length, kPointsPerInch, kMicronsPerInch)); - if (paper_size_microns.width() > 0 && paper_size_microns.height() > 0) { + if (!paper_size_microns.IsEmpty()) { PrinterSemanticCapsAndDefaults::Paper paper; paper.size_um = paper_size_microns; paper.vendor_id = ppd->sizes[i].name; @@ -492,11 +495,33 @@ bool ParsePpdCapabilities(base::StringPiece printer_name, } } caps.papers.push_back(paper); - if (i == 0 || ppd->sizes[i].marked) { + if (ppd->sizes[i].marked) { caps.default_paper = paper; + is_default_found = true; } } } + if (!is_default_found) { + gfx::Size locale_paper_microns = + GetDefaultPaperSizeFromLocaleMicrons(locale); + for (const PrinterSemanticCapsAndDefaults::Paper& paper : caps.papers) { + // Set epsilon to 500 microns to allow tolerance of rounded paper sizes. + // While the above utility function returns paper sizes in microns, they + // are still rounded to the nearest millimeter (1000 microns). + constexpr int kSizeEpsilon = 500; + if (SizesEqualWithinEpsilon(paper.size_um, locale_paper_microns, + kSizeEpsilon)) { + caps.default_paper = paper; + is_default_found = true; + break; + } + } + + // If no default was set in the PPD or if the locale default is not within + // the printer's capabilities, select the first on the list. + if (!is_default_found) + caps.default_paper = caps.papers[0]; + } } ppdClose(ppd); diff --git a/printing/backend/cups_helper.h b/printing/backend/cups_helper.h index b72903ff89dee0..93a4d79f7e5005 100644 --- a/printing/backend/cups_helper.h +++ b/printing/backend/cups_helper.h @@ -38,6 +38,7 @@ class PRINTING_EXPORT HttpConnectionCUPS { // semantic options. PRINTING_EXPORT bool ParsePpdCapabilities( base::StringPiece printer_name, + base::StringPiece locale, base::StringPiece printer_capabilities, PrinterSemanticCapsAndDefaults* printer_info); diff --git a/printing/backend/cups_helper_unittest.cc b/printing/backend/cups_helper_unittest.cc index 049a64746d146e..de7ebe0e71032a 100644 --- a/printing/backend/cups_helper_unittest.cc +++ b/printing/backend/cups_helper_unittest.cc @@ -3,10 +3,23 @@ // found in the LICENSE file. #include "printing/backend/cups_helper.h" + #include "printing/backend/print_backend.h" +#include "printing/printing_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { + +// Returns true if the papers have the same name, vendor ID, and size. +bool PapersEqual(const printing::PrinterSemanticCapsAndDefaults::Paper& lhs, + const printing::PrinterSemanticCapsAndDefaults::Paper& rhs) { + return lhs.display_name == rhs.display_name && + lhs.vendor_id == rhs.vendor_id && lhs.size_um == rhs.size_um; +} + +} // namespace + TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexShortEdge) { const char kTestPpdData[] = "*PPD-Adobe: \"4.3\"\n\n" @@ -32,7 +45,7 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexShortEdge) { "*CloseGroup: General\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE(printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.collate_capable); EXPECT_TRUE(caps.collate_default); EXPECT_TRUE(caps.copies_capable); @@ -61,7 +74,7 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexSimples) { "*CloseGroup: General\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE(printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.collate_capable); EXPECT_TRUE(caps.collate_default); EXPECT_TRUE(caps.copies_capable); @@ -89,7 +102,7 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorNoDuplex) { "*CloseGroup: General\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE(printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.collate_capable); EXPECT_TRUE(caps.collate_default); EXPECT_TRUE(caps.copies_capable); @@ -124,7 +137,7 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingColorTrueDuplexShortEdge) { "*CloseGroup: General\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE(printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.collate_capable); EXPECT_TRUE(caps.collate_default); EXPECT_TRUE(caps.copies_capable); @@ -165,7 +178,7 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingColorFalseDuplexLongEdge) { "*CloseGroup: General\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE(printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.collate_capable); EXPECT_TRUE(caps.collate_default); EXPECT_TRUE(caps.copies_capable); @@ -181,7 +194,7 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingPageSize) { const char kTestPpdData[] = "*PPD-Adobe: \"4.3\"\n\n" "*OpenUI *PageSize: PickOne\n" - "*DefaultPageSize: Letter\n" + "*DefaultPageSize: Legal\n" "*PageSize Letter/US Letter: \"" " <> setpagedevice\"\n" @@ -190,13 +203,13 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingPageSize) { " <> setpagedevice\"\n" "*End\n" - "*DefaultPaperDimension: Letter\n" + "*DefaultPaperDimension: Legal\n" "*PaperDimension Letter/US Letter: \"612 792\"\n" "*PaperDimension Legal/US Legal: \"612 1008\"\n\n" "*CloseUI: *PageSize\n\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE(printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); ASSERT_EQ(2UL, caps.papers.size()); EXPECT_EQ("Letter", caps.papers[0].vendor_id); EXPECT_EQ("US Letter", caps.papers[0].display_name); @@ -206,6 +219,57 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingPageSize) { EXPECT_EQ("US Legal", caps.papers[1].display_name); EXPECT_EQ(215900, caps.papers[1].size_um.width()); EXPECT_EQ(355600, caps.papers[1].size_um.height()); + EXPECT_TRUE(PapersEqual(caps.papers[1], caps.default_paper)); +} + +TEST(PrintBackendCupsHelperTest, TestPpdParsingPageSizeNoDefaultSpecified) { + const char kTestPpdData[] = + R"(*PPD-Adobe: "4.3" +*OpenUI *PageSize: PickOne +*PageSize A3/ISO A3: " + << /DeferredMediaSelection true /PageSize [842 1191] + /ImagingBBox null >> setpagedevice" +*End +*PageSize A4/ISO A4: " + << /DeferredMediaSelection true /PageSize [595 842] + /ImagingBBox null >> setpagedevice" +*End +*PageSize Legal/US Legal: " + << /DeferredMediaSelection true /PageSize [612 1008] + /ImagingBBox null >> setpagedevice" +*End +*PageSize Letter/US Letter: " + << /DeferredMediaSelection true /PageSize [612 792] + /ImagingBBox null >> setpagedevice" +*End +*PaperDimension A3/ISO A3: "842 1191" +*PaperDimension A4/ISO A4: "595 842" +*PaperDimension Legal/US Legal: "612 1008" +*PaperDimension Letter/US Letter: "612 792" +*CloseUI: *PageSize)"; + + { + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE( + printing::ParsePpdCapabilities("test", "en-US", kTestPpdData, &caps)); + ASSERT_EQ(4UL, caps.papers.size()); + EXPECT_EQ("Letter", caps.papers[3].vendor_id); + EXPECT_EQ("US Letter", caps.papers[3].display_name); + EXPECT_EQ(215900, caps.papers[3].size_um.width()); + EXPECT_EQ(279400, caps.papers[3].size_um.height()); + EXPECT_TRUE(PapersEqual(caps.papers[3], caps.default_paper)); + } + { + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE( + printing::ParsePpdCapabilities("test", "en-UK", kTestPpdData, &caps)); + ASSERT_EQ(4UL, caps.papers.size()); + EXPECT_EQ("A4", caps.papers[1].vendor_id); + EXPECT_EQ("ISO A4", caps.papers[1].display_name); + EXPECT_EQ(209903, caps.papers[1].size_um.width()); + EXPECT_EQ(297039, caps.papers[1].size_um.height()); + EXPECT_TRUE(PapersEqual(caps.papers[1], caps.default_paper)); + } } TEST(PrintBackendCupsHelperTest, TestPpdParsingBrotherPrinters) { @@ -221,7 +285,8 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingBrotherPrinters) { "*CloseUI: *BRPrintQuality\n\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE( + printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.color_changeable); EXPECT_TRUE(caps.color_default); EXPECT_EQ(printing::BROTHER_BRSCRIPT3_COLOR, caps.color_model); @@ -239,7 +304,8 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingBrotherPrinters) { "*CloseUI: *BRMonoColor\n\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE( + printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.color_changeable); EXPECT_TRUE(caps.color_default); EXPECT_EQ(printing::BROTHER_CUPS_COLOR, caps.color_model); @@ -257,7 +323,8 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingBrotherPrinters) { "*CloseUI: *BRDuplex\n\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE( + printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_THAT(caps.duplex_modes, testing::UnorderedElementsAre( printing::SIMPLEX, printing::LONG_EDGE, printing::SHORT_EDGE)); @@ -277,7 +344,8 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingSamsungPrinters) { "*CloseUI: *ColorMode\n\n"; printing::PrinterSemanticCapsAndDefaults caps; - EXPECT_TRUE(printing::ParsePpdCapabilities("test", kTestPpdData, &caps)); + EXPECT_TRUE( + printing::ParsePpdCapabilities("test", "", kTestPpdData, &caps)); EXPECT_TRUE(caps.color_changeable); EXPECT_TRUE(caps.color_default); EXPECT_EQ(printing::COLORMODE_COLOR, caps.color_model); diff --git a/printing/backend/print_backend_cups.cc b/printing/backend/print_backend_cups.cc index d5d4da9d3809bd..c8218a00dad68b 100644 --- a/printing/backend/print_backend_cups.cc +++ b/printing/backend/print_backend_cups.cc @@ -158,7 +158,7 @@ bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults( if (!GetPrinterCapsAndDefaults(printer_name, &info)) return false; - return ParsePpdCapabilities(printer_name, info.printer_capabilities, + return ParsePpdCapabilities(printer_name, locale(), info.printer_capabilities, printer_info); } diff --git a/printing/printing_utils.cc b/printing/printing_utils.cc index 353a0aab1e9a4a..96ae1ef751d4f6 100644 --- a/printing/printing_utils.cc +++ b/printing/printing_utils.cc @@ -4,21 +4,26 @@ #include "printing/printing_utils.h" -#include +#include #include +#include +#include #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "printing/units.h" #include "third_party/icu/source/common/unicode/uchar.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/text_elider.h" namespace printing { namespace { -const size_t kMaxDocumentTitleLength = 80; +constexpr size_t kMaxDocumentTitleLength = 80; +constexpr gfx::Size kIsoA4Microns = gfx::Size(210000, 297000); } // namespace @@ -63,4 +68,35 @@ base::string16 FormatDocumentTitleWithOwner(const base::string16& owner, kMaxDocumentTitleLength); } +gfx::Size GetDefaultPaperSizeFromLocaleMicrons(base::StringPiece locale) { + if (locale.empty()) + return kIsoA4Microns; + + int32_t width = 0; + int32_t height = 0; + UErrorCode error = U_ZERO_ERROR; + ulocdata_getPaperSize(locale.as_string().c_str(), &height, &width, &error); + if (error > U_ZERO_ERROR) { + // If the call failed, assume Letter paper size. + LOG(WARNING) << "ulocdata_getPaperSize failed, using ISO A4 Paper, error: " + << error; + + return kIsoA4Microns; + } + // Convert millis to microns + return gfx::Size(width * 1000, height * 1000); +} + +bool SizesEqualWithinEpsilon(const gfx::Size& lhs, + const gfx::Size& rhs, + int epsilon) { + DCHECK_GE(epsilon, 0); + + if (lhs.IsEmpty() && rhs.IsEmpty()) + return true; + + return std::abs(lhs.width() - rhs.width()) <= epsilon && + std::abs(lhs.height() - rhs.height()) <= epsilon; +} + } // namespace printing diff --git a/printing/printing_utils.h b/printing/printing_utils.h index b0e1f921335cc6..332b5833c35acd 100644 --- a/printing/printing_utils.h +++ b/printing/printing_utils.h @@ -8,8 +8,13 @@ #include #include "base/strings/string16.h" +#include "base/strings/string_piece.h" #include "printing/printing_export.h" +namespace gfx { +class Size; +} + namespace printing { // Simplify title to resolve issue with some drivers. @@ -29,6 +34,17 @@ PRINTING_EXPORT base::string16 FormatDocumentTitleWithOwnerAndLength( const base::string16& title, size_t length); +// Returns the paper size (microns) most common in the locale to the nearest +// millimeter. Defaults to ISO A4 for an empty or invalid locale. +PRINTING_EXPORT gfx::Size GetDefaultPaperSizeFromLocaleMicrons( + base::StringPiece locale); + +// Returns true if both dimensions of the sizes have a delta less than or equal +// to the epsilon value. +PRINTING_EXPORT bool SizesEqualWithinEpsilon(const gfx::Size& lhs, + const gfx::Size& rhs, + int epsilon); + } // namespace printing #endif // PRINTING_PRINTING_UTILS_H_ diff --git a/printing/printing_utils_unittest.cc b/printing/printing_utils_unittest.cc index 4a0442760d760b..54154aa1d15ee1 100644 --- a/printing/printing_utils_unittest.cc +++ b/printing/printing_utils_unittest.cc @@ -2,17 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "printing/printing_utils.h" + #include +#include +#include + #include "base/strings/utf_string_conversions.h" -#include "printing/printing_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/size.h" namespace printing { namespace { -const size_t kTestLength = 8; +constexpr size_t kTestLength = 8; +constexpr gfx::Size kIsoA4Microns(210000, 297000); +constexpr gfx::Size kNaLetterMicrons(216000, 279000); std::string Simplify(const std::string& title) { return base::UTF16ToUTF8( @@ -46,4 +53,47 @@ TEST(PrintingUtilsTest, FormatDocumentTitleWithOwner) { EXPECT_EQ("ab...j: ", Format("abcdefghij", "0123456789")); } +TEST(PrintingUtilsTest, GetDefaultPaperSizeFromLocaleMicrons) { + // Valid locales + EXPECT_EQ(kNaLetterMicrons, GetDefaultPaperSizeFromLocaleMicrons("en-US")); + EXPECT_EQ(kNaLetterMicrons, GetDefaultPaperSizeFromLocaleMicrons("en_US")); + EXPECT_EQ(kNaLetterMicrons, GetDefaultPaperSizeFromLocaleMicrons("fr-CA")); + EXPECT_EQ(kNaLetterMicrons, GetDefaultPaperSizeFromLocaleMicrons("es-CL")); + EXPECT_EQ(kIsoA4Microns, GetDefaultPaperSizeFromLocaleMicrons("en_UK")); + EXPECT_EQ(kIsoA4Microns, GetDefaultPaperSizeFromLocaleMicrons("fa-IR")); + + // Empty locale + EXPECT_EQ(kIsoA4Microns, GetDefaultPaperSizeFromLocaleMicrons("")); + + // Non-existing locale + EXPECT_EQ(kIsoA4Microns, + GetDefaultPaperSizeFromLocaleMicrons("locale-does-not-exist")); +} + +TEST(PrintingUtilsTest, SizesEqualWithinEpsilon) { + constexpr int kMaxInt = std::numeric_limits::max(); + + // Large sizes + EXPECT_TRUE(SizesEqualWithinEpsilon(gfx::Size(kMaxInt, kMaxInt), + gfx::Size(kMaxInt - 1, kMaxInt - 1), 1)); + EXPECT_FALSE(SizesEqualWithinEpsilon(gfx::Size(kMaxInt, kMaxInt), + gfx::Size(kMaxInt - 1, kMaxInt - 2), 1)); + EXPECT_TRUE(SizesEqualWithinEpsilon(gfx::Size(kMaxInt, kMaxInt), + gfx::Size(0, 0), kMaxInt)); + EXPECT_FALSE(SizesEqualWithinEpsilon(gfx::Size(kMaxInt, kMaxInt), + gfx::Size(0, 0), kMaxInt - 1)); + + // Empty sizes + EXPECT_TRUE(SizesEqualWithinEpsilon(gfx::Size(0, 0), gfx::Size(0, 0), 0)); + EXPECT_TRUE(SizesEqualWithinEpsilon(gfx::Size(1, 0), gfx::Size(0, 2), 0)); + EXPECT_TRUE(SizesEqualWithinEpsilon(gfx::Size(1, -2), gfx::Size(-1, 2), 0)); + + // Common paper sizes + EXPECT_FALSE(SizesEqualWithinEpsilon(kNaLetterMicrons, kIsoA4Microns, 1000)); + EXPECT_TRUE(SizesEqualWithinEpsilon(kNaLetterMicrons, + gfx::Size(215900, 279400), 500)); + EXPECT_TRUE( + SizesEqualWithinEpsilon(kIsoA4Microns, gfx::Size(210500, 296500), 500)); +} + } // namespace printing diff --git a/printing/units.h b/printing/units.h index 7b83577a8f7401..6dcc2062d5258f 100644 --- a/printing/units.h +++ b/printing/units.h @@ -18,30 +18,30 @@ constexpr int kMilsPerInch = 1000; // Length of an inch in CSS's 1pt unit. // http://dev.w3.org/csswg/css3-values/#absolute-length-units-cm-mm.-in-pt-pc -const int kPointsPerInch = 72; +constexpr int kPointsPerInch = 72; // Length of an inch in CSS's 1px unit. // http://dev.w3.org/csswg/css3-values/#the-px-unit -const int kPixelsPerInch = 96; +constexpr int kPixelsPerInch = 96; // Dpi used to save to PDF or Cloud Print. -const int kDefaultPdfDpi = 300; +constexpr int kDefaultPdfDpi = 300; // LETTER: 8.5 x 11 inches -const float kLetterWidthInch = 8.5f; -const float kLetterHeightInch = 11.0f; +constexpr float kLetterWidthInch = 8.5f; +constexpr float kLetterHeightInch = 11.0f; // LEGAL: 8.5 x 14 inches -const float kLegalWidthInch = 8.5f; -const float kLegalHeightInch = 14.0f; +constexpr float kLegalWidthInch = 8.5f; +constexpr float kLegalHeightInch = 14.0f; // A4: 8.27 x 11.69 inches -const float kA4WidthInch = 8.27f; -const float kA4HeightInch = 11.69f; +constexpr float kA4WidthInch = 8.27f; +constexpr float kA4HeightInch = 11.69f; // A3: 11.69 x 16.54 inches -const float kA3WidthInch = 11.69f; -const float kA3HeightInch = 16.54f; +constexpr float kA3WidthInch = 11.69f; +constexpr float kA3HeightInch = 16.54f; // Converts from one unit system to another using integer arithmetics. PRINTING_EXPORT int ConvertUnit(double value, int old_unit, int new_unit);