diff --git a/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc b/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc
index 7807d0b59de9b..1e23fb279810b 100644
--- a/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc
@@ -56,6 +56,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 
@@ -228,7 +229,7 @@ class TestLocalPrinterAshWithPrinterConfigurer : public TestLocalPrinterAsh {
 class LocalPrinterAshTestBase : public testing::Test {
  public:
   const std::vector<PrinterSemanticCapsAndDefaults::Paper> kPapers = {
-      {"bar", "vendor", {600, 600}}};
+      {"bar", "vendor", gfx::Size(600, 600), gfx::Rect(0, 0, 600, 600)}};
 
   LocalPrinterAshTestBase() = default;
   LocalPrinterAshTestBase(const LocalPrinterAshTestBase&) = delete;
@@ -1035,7 +1036,7 @@ TEST(LocalPrinterAsh, StatusToMojom) {
 class LocalPrinterAshWithOAuth2Test : public testing::Test {
  public:
   const std::vector<PrinterSemanticCapsAndDefaults::Paper> kPapers = {
-      {"bar", "vendor", {600, 600}}};
+      {"bar", "vendor", gfx::Size(600, 600), gfx::Rect(0, 0, 600, 600)}};
 
   LocalPrinterAshWithOAuth2Test() = default;
   LocalPrinterAshWithOAuth2Test(const LocalPrinterAshWithOAuth2Test&) = delete;
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
index 425fc8fae49bc..a0fb70b3bda53 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
@@ -24,6 +24,8 @@
 #include "printing/print_job_constants.h"
 #include "printing/printing_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
 
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
 #include "chrome/browser/printing/print_backend_service_manager.h"
@@ -294,8 +296,8 @@ class LocalPrinterHandlerDefaultTestBase : public testing::Test {
                   bool is_default,
                   bool requires_elevated_permissions) {
     auto caps = std::make_unique<PrinterSemanticCapsAndDefaults>();
-    caps->papers.emplace_back(
-        PrinterSemanticCapsAndDefaults::Paper{"bar", "vendor", {600, 600}});
+    caps->papers.emplace_back(PrinterSemanticCapsAndDefaults::Paper{
+        "bar", "vendor", gfx::Size(600, 600), gfx::Rect(0, 0, 600, 600)});
     auto basic_info = std::make_unique<PrinterBasicInfo>(
         id, display_name, description,
         /*printer_status=*/0, is_default, PrinterBasicInfoOptions{});
diff --git a/chrome/common/printing/print_media_l10n_unittest.cc b/chrome/common/printing/print_media_l10n_unittest.cc
index c04bd92f81865..870264c2fcd49 100644
--- a/chrome/common/printing/print_media_l10n_unittest.cc
+++ b/chrome/common/printing/print_media_l10n_unittest.cc
@@ -9,7 +9,10 @@
 #include <vector>
 
 #include "chrome/common/printing/print_media_l10n.h"
+#include "printing/backend/print_backend.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace printing {
 
@@ -137,14 +140,18 @@ TEST(PrintMediaL10N, SortGroupsOrdered) {
 
 // Verifies that inch paper sizes are sorted by width, height, name.
 TEST(PrintMediaL10N, SortInchSizes) {
-  PaperWithSizeInfo p1 = {MediaSizeInfo{u"1x3", MediaSizeGroup::kSizeIn},
-                          Paper{"1x3", "in", gfx::Size(1, 3)}};
-  PaperWithSizeInfo p2 = {MediaSizeInfo{u"2x1", MediaSizeGroup::kSizeIn},
-                          Paper{"2x1", "in", gfx::Size(2, 1)}};
-  PaperWithSizeInfo p3 = {MediaSizeInfo{u"2x2", MediaSizeGroup::kSizeIn},
-                          Paper{"2x2", "in", gfx::Size(2, 2)}};
-  PaperWithSizeInfo p4 = {MediaSizeInfo{u"2x2 B", MediaSizeGroup::kSizeIn},
-                          Paper{"2x2 B", "in", gfx::Size(2, 2)}};
+  PaperWithSizeInfo p1 = {
+      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},
+      Paper{"2x1", "in", gfx::Size(2, 1), gfx::Rect(0, 0, 2, 1)}};
+  PaperWithSizeInfo p3 = {
+      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},
+      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};
@@ -156,14 +163,18 @@ TEST(PrintMediaL10N, SortInchSizes) {
 
 // Verifies that mm paper sizes are sorted by width, height, name.
 TEST(PrintMediaL10N, SortMmSizes) {
-  PaperWithSizeInfo p1 = {MediaSizeInfo{u"1x3", MediaSizeGroup::kSizeMm},
-                          Paper{"1x3", "mm", gfx::Size(1, 3)}};
-  PaperWithSizeInfo p2 = {MediaSizeInfo{u"2x1", MediaSizeGroup::kSizeMm},
-                          Paper{"2x1", "mm", gfx::Size(2, 1)}};
-  PaperWithSizeInfo p3 = {MediaSizeInfo{u"2x2", MediaSizeGroup::kSizeMm},
-                          Paper{"2x2", "mm", gfx::Size(2, 2)}};
-  PaperWithSizeInfo p4 = {MediaSizeInfo{u"2x2 B", MediaSizeGroup::kSizeMm},
-                          Paper{"2x2 B", "mm", gfx::Size(2, 2)}};
+  PaperWithSizeInfo p1 = {
+      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},
+      Paper{"2x1", "mm", gfx::Size(2, 1), gfx::Rect(0, 0, 2, 1)}};
+  PaperWithSizeInfo p3 = {
+      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},
+      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};
@@ -175,14 +186,18 @@ TEST(PrintMediaL10N, SortMmSizes) {
 
 // Verifies that named paper sizes are sorted by name, width, height.
 TEST(PrintMediaL10N, SortNamedSizes) {
-  PaperWithSizeInfo p1 = {MediaSizeInfo{u"AAA", MediaSizeGroup::kSizeNamed},
-                          Paper{"AAA", "name", gfx::Size(50, 50)}};
-  PaperWithSizeInfo p2 = {MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed},
-                          Paper{"BBB", "name1", gfx::Size(1, 3)}};
-  PaperWithSizeInfo p3 = {MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed},
-                          Paper{"BBB", "name2", gfx::Size(2, 2)}};
-  PaperWithSizeInfo p4 = {MediaSizeInfo{u"BBB", MediaSizeGroup::kSizeNamed},
-                          Paper{"BBB", "name3", gfx::Size(2, 3)}};
+  PaperWithSizeInfo p1 = {
+      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},
+      Paper{"BBB", "name1", gfx::Size(1, 3), gfx::Rect(0, 0, 1, 3)}};
+  PaperWithSizeInfo p3 = {
+      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},
+      Paper{"BBB", "name3", gfx::Size(2, 3), gfx::Rect(0, 0, 2, 3)}};
 
   std::vector<PaperWithSizeInfo> papers = {p4, p1, p2, p3};
   std::vector<PaperWithSizeInfo> expected = {p1, p2, p3, p4};
diff --git a/printing/backend/mojom/print_backend.mojom b/printing/backend/mojom/print_backend.mojom
index f9c3d91d4e121..bed5f843e38bb 100644
--- a/printing/backend/mojom/print_backend.mojom
+++ b/printing/backend/mojom/print_backend.mojom
@@ -22,11 +22,13 @@ struct PrinterBasicInfo {
 // Paper used by printer semantic capabilities and defaults.
 // Corresponds to `printing::PrinterSemanticCapsAndDefaults::Paper` in
 // printing/backend/print_backend.h.
+// For backwards compatibility, `printable_area_um` must be optional.
 [Stable]
 struct Paper {
   string display_name;
   string vendor_id;
   gfx.mojom.Size size_um;
+  [MinVersion=1] gfx.mojom.Rect? printable_area_um;
 };
 
 // An advanced capability value for ChromeOS printing.
diff --git a/printing/backend/mojom/print_backend_mojom_traits.cc b/printing/backend/mojom/print_backend_mojom_traits.cc
index 6e054e03002df..cb24a5bba1af4 100644
--- a/printing/backend/mojom/print_backend_mojom_traits.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits.cc
@@ -112,8 +112,30 @@ bool StructTraits<printing::mojom::PaperDataView,
                   printing::PrinterSemanticCapsAndDefaults::Paper>::
     Read(printing::mojom::PaperDataView data,
          printing::PrinterSemanticCapsAndDefaults::Paper* out) {
-  return data.ReadDisplayName(&out->display_name) &&
-         data.ReadVendorId(&out->vendor_id) && data.ReadSizeUm(&out->size_um);
+  absl::optional<gfx::Rect> printable_area_um;
+  if (!data.ReadDisplayName(&out->display_name) ||
+      !data.ReadVendorId(&out->vendor_id) || !data.ReadSizeUm(&out->size_um) ||
+      !data.ReadPrintableAreaUm(&printable_area_um)) {
+    return false;
+  }
+
+  // For backwards compatibility, allow printable area to be missing. Set the
+  // default printable area to be the page size.
+  out->printable_area_um = printable_area_um.value_or(gfx::Rect(out->size_um));
+
+  // Allow empty Papers, since PrinterSemanticCapsAndDefaults can have empty
+  // default Papers.
+  if (out->display_name.empty() && out->vendor_id.empty() &&
+      out->size_um.IsEmpty() && out->printable_area_um.IsEmpty()) {
+    return true;
+  }
+
+  // Invalid if the printable area is empty, has negative values or has a larger
+  // width or height than the size of the Paper.
+  return !out->printable_area_um.IsEmpty() && out->printable_area_um.x() >= 0 &&
+         out->printable_area_um.y() >= 0 &&
+         out->printable_area_um.width() <= out->size_um.width() &&
+         out->printable_area_um.height() <= out->size_um.height();
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/printing/backend/mojom/print_backend_mojom_traits.h b/printing/backend/mojom/print_backend_mojom_traits.h
index 5e2aba638c59c..f562a64ebfc3b 100644
--- a/printing/backend/mojom/print_backend_mojom_traits.h
+++ b/printing/backend/mojom/print_backend_mojom_traits.h
@@ -12,6 +12,7 @@
 #include "printing/backend/mojom/print_backend.mojom-shared.h"
 #include "printing/backend/print_backend.h"
 #include "printing/mojom/print.mojom.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace mojo {
@@ -59,6 +60,10 @@ struct StructTraits<printing::mojom::PaperDataView,
       const printing::PrinterSemanticCapsAndDefaults::Paper& p) {
     return p.size_um;
   }
+  static const gfx::Rect& printable_area_um(
+      const printing::PrinterSemanticCapsAndDefaults::Paper& p) {
+    return p.printable_area_um;
+  }
 
   static bool Read(printing::mojom::PaperDataView data,
                    printing::PrinterSemanticCapsAndDefaults::Paper* out);
diff --git a/printing/backend/mojom/print_backend_mojom_traits_unittest.cc b/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
index a2ad736371ce5..7451fb09185bd 100644
--- a/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
@@ -88,6 +88,52 @@ TEST(PrintBackendMojomTraitsTest, TestSerializeAndDeserializePaper) {
   }
 }
 
+TEST(PrintBackendMojomTraitsTest, TestPaperEmpty) {
+  // Empty Papers should be valid.
+  PrinterSemanticCapsAndDefaults::Paper input;
+  PrinterSemanticCapsAndDefaults::Paper output;
+
+  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
+  EXPECT_EQ(input, output);
+}
+
+TEST(PrintBackendMojomTraitsTest, TestPaperEmptyPrintableArea) {
+  // The printable area is empty, but the other fields are not, so it should be
+  // invalid.
+  PrinterSemanticCapsAndDefaults::Paper input{
+      /*display_name=*/"display_name", /*vendor_id=*/"vendor_id",
+      /*size_um=*/gfx::Size(4000, 7000),
+      /*printable_area_um=*/gfx::Rect(0, 100, 0, 0)};
+  PrinterSemanticCapsAndDefaults::Paper output;
+
+  EXPECT_FALSE(
+      mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
+}
+
+TEST(PrintBackendMojomTraitsTest, TestPaperPrintableAreaLargerThanSize) {
+  // The printable area is larger than the size, so it should be invalid.
+  PrinterSemanticCapsAndDefaults::Paper input{
+      /*display_name=*/"display_name", /*vendor_id=*/"vendor_id",
+      /*size_um=*/gfx::Size(4000, 7000),
+      /*printable_area_um=*/gfx::Rect(0, 100, 4100, 7200)};
+  PrinterSemanticCapsAndDefaults::Paper output;
+
+  EXPECT_FALSE(
+      mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
+}
+
+TEST(PrintBackendMojomTraitsTest, TestPaperNegativePrintableArea) {
+  // The printable area has negative x and y values, so it should be invalid.
+  PrinterSemanticCapsAndDefaults::Paper input{
+      /*display_name=*/"display_name", /*vendor_id=*/"vendor_id",
+      /*size_um=*/gfx::Size(4000, 7000),
+      /*printable_area_um=*/gfx::Rect(-10, -10, 2800, 6000)};
+  PrinterSemanticCapsAndDefaults::Paper output;
+
+  EXPECT_FALSE(
+      mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
+}
+
 #if BUILDFLAG(IS_CHROMEOS)
 TEST(PrintBackendMojomTraitsTest,
      TestSerializeAndDeserializeAdvancedCapability) {
diff --git a/printing/backend/print_backend.cc b/printing/backend/print_backend.cc
index f8ac14d71faa8..59b1849eaaffa 100644
--- a/printing/backend/print_backend.cc
+++ b/printing/backend/print_backend.cc
@@ -157,7 +157,8 @@ XpsCapabilities::~XpsCapabilities() = default;
 bool PrinterSemanticCapsAndDefaults::Paper::operator==(
     const PrinterSemanticCapsAndDefaults::Paper& other) const {
   return display_name == other.display_name && vendor_id == other.vendor_id &&
-         size_um == other.size_um;
+         size_um == other.size_um &&
+         printable_area_um == other.printable_area_um;
 }
 
 PrinterSemanticCapsAndDefaults::PrinterSemanticCapsAndDefaults() = default;
diff --git a/printing/backend/print_backend.h b/printing/backend/print_backend.h
index de71d366af21d..888771dd1e143 100644
--- a/printing/backend/print_backend.h
+++ b/printing/backend/print_backend.h
@@ -18,6 +18,7 @@
 #include "build/build_config.h"
 #include "printing/mojom/print.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -192,6 +193,9 @@ struct COMPONENT_EXPORT(PRINT_BACKEND) PrinterSemanticCapsAndDefaults {
     std::string vendor_id;
     gfx::Size size_um;
 
+    // Origin (x,y) is at the bottom-left.
+    gfx::Rect printable_area_um;
+
     bool operator==(const Paper& other) const;
   };
   using Papers = std::vector<Paper>;
diff --git a/printing/backend/print_backend_test_constants.h b/printing/backend/print_backend_test_constants.h
index 221558015082b..fd4fd608b6542 100644
--- a/printing/backend/print_backend_test_constants.h
+++ b/printing/backend/print_backend_test_constants.h
@@ -40,16 +40,20 @@ struct OptionalSampleCapabilities {
 
 inline const PrinterSemanticCapsAndDefaults::Paper kPaperA3{
     /*display_name=*/"A3", /*vendor_id=*/"67",
-    /*size_um=*/gfx::Size(7016, 9921)};
+    /*size_um=*/gfx::Size(7016, 9921),
+    /*printable_area_um=*/gfx::Rect(0, 0, 7016, 9921)};
 inline const PrinterSemanticCapsAndDefaults::Paper kPaperA4{
     /*display_name=*/"A4", /*vendor_id=*/"12",
-    /*size_um=*/gfx::Size(4961, 7016)};
+    /*size_um=*/gfx::Size(4961, 7016),
+    /*printable_area_um=*/gfx::Rect(100, 200, 500, 800)};
 inline const PrinterSemanticCapsAndDefaults::Paper kPaperLetter{
     /*display_name=*/"Letter", /*vendor_id=*/"45",
-    /*size_um=*/gfx::Size(5100, 6600)};
+    /*size_um=*/gfx::Size(5100, 6600),
+    /*printable_area_um=*/gfx::Rect(0, 0, 5100, 6600)};
 inline const PrinterSemanticCapsAndDefaults::Paper kPaperLedger{
     /*display_name=*/"Ledger", /*vendor_id=*/"89",
-    /*size_um=*/gfx::Size(6600, 10200)};
+    /*size_um=*/gfx::Size(6600, 10200),
+    /*printable_area_um=*/gfx::Rect(0, 0, 6600, 10200)};
 
 #if BUILDFLAG(IS_CHROMEOS)
 inline const AdvancedCapability kAdvancedCapability1(
diff --git a/printing/backend/print_backend_utils.cc b/printing/backend/print_backend_utils.cc
index 4032ea266cc01..e1590d2aa9f1e 100644
--- a/printing/backend/print_backend_utils.cc
+++ b/printing/backend/print_backend_utils.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "printing/units.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/size_f.h"
@@ -109,6 +110,7 @@ PrinterSemanticCapsAndDefaults::Paper ParsePaper(base::StringPiece value) {
   PrinterSemanticCapsAndDefaults::Paper paper;
   paper.vendor_id = std::string(value);
   paper.size_um = DimensionsToMicrons(dimensions);
+  paper.printable_area_um = gfx::Rect(paper.size_um);
   // Omits the final token describing the media dimensions.
   pieces.pop_back();
   paper.display_name = base::JoinString(pieces, " ");