chromium/printing/backend/xps_utils_win.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/backend/xps_utils_win.h"

#include <utility>

#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "printing/backend/print_backend.h"
#include "printing/mojom/print.mojom.h"
#include "services/data_decoder/public/cpp/safe_xml_parser.h"

namespace printing {

namespace {

// Elements and namespaces in XML data. The order of these elements follows
// the Print Schema Framework elements order. Details can be found here:
// https://docs.microsoft.com/en-us/windows/win32/printdocs/details-of-the-printcapabilities-schema
constexpr char kPrintCapabilities[] = "psf:PrintCapabilities";
constexpr char kFeature[] = "psf:Feature";
constexpr char kPageOutputQuality[] = "psk:PageOutputQuality";
constexpr char kOption[] = "psf:Option";
constexpr char kProperty[] = "psf:Property";
constexpr char kValue[] = "psf:Value";
constexpr char kName[] = "name";

base::expected<PageOutputQuality, mojom::ResultCode> LoadPageOutputQuality(
    const base::Value& page_output_quality) {
  PageOutputQuality printer_page_output_quality;
  std::vector<const base::Value*> options;
  data_decoder::GetAllXmlElementChildrenWithTag(page_output_quality, kOption,
                                                &options);
  if (options.empty()) {
    LOG(WARNING) << "Incorrect XML format";
    return base::unexpected(mojom::ResultCode::kFailed);
  }
  for (const auto* option : options) {
    PageOutputQualityAttribute quality;
    quality.name = data_decoder::GetXmlElementAttribute(*option, kName);
    int property_count =
        data_decoder::GetXmlElementChildrenCount(*option, kProperty);

    // TODO(crbug.com/40212677): Each formatted option is expected to have zero
    // or one property. Each property inside an option is expected to
    // have one value.
    // Source:
    // https://docs.microsoft.com/en-us/windows/win32/printdocs/pageoutputquality
    // If an option has more than one property or a property has more than one
    // value, more work is expected here.

    // In the case an option looks like <psf:Option name="psk:Text />,
    // property_count is 0. In this case, an option only has `name`
    // and does not have `display_name`.
    if (property_count > 1) {
      LOG(WARNING) << "Incorrect XML format";
      return base::unexpected(mojom::ResultCode::kFailed);
    }
    if (property_count == 1) {
      const base::Value* property_element = data_decoder::FindXmlElementPath(
          *option, {kOption, kProperty}, /*unique_path=*/nullptr);
      int value_count =
          data_decoder::GetXmlElementChildrenCount(*property_element, kValue);
      if (value_count != 1) {
        LOG(WARNING) << "Incorrect XML format";
        return base::unexpected(mojom::ResultCode::kFailed);
      }
      const base::Value* value_element = data_decoder::FindXmlElementPath(
          *option, {kOption, kProperty, kValue}, /*unique_path=*/nullptr);
      std::string text;
      data_decoder::GetXmlElementText(*value_element, &text);
      quality.display_name = std::move(text);
    }
    printer_page_output_quality.qualities.push_back(std::move(quality));
  }
  return printer_page_output_quality;
}

}  // namespace

base::expected<XpsCapabilities, mojom::ResultCode>
ParseValueForXpsPrinterCapabilities(const base::Value& capabilities) {
  if (!data_decoder::IsXmlElementNamed(capabilities, kPrintCapabilities)) {
    LOG(WARNING) << "Incorrect XML format";
    return base::unexpected(mojom::ResultCode::kFailed);
  }
  std::vector<const base::Value*> features;
  data_decoder::GetAllXmlElementChildrenWithTag(capabilities, kFeature,
                                                &features);
  if (features.empty()) {
    LOG(WARNING) << "Incorrect XML format";
    return base::unexpected(mojom::ResultCode::kFailed);
  }
  XpsCapabilities xps_capabilities;
  for (auto* feature : features) {
    std::string feature_name =
        data_decoder::GetXmlElementAttribute(*feature, kName);
    DVLOG(2) << feature_name;
    if (feature_name == kPageOutputQuality) {
      ASSIGN_OR_RETURN(xps_capabilities.page_output_quality,
                       LoadPageOutputQuality(*feature));
    }

    // TODO(crbug.com/40212677): Each feature needs to be parsed. More work is
    // expected here.
  }
  return xps_capabilities;
}

void MergeXpsCapabilities(
    XpsCapabilities xps_capabilities,
    PrinterSemanticCapsAndDefaults& printer_capabilities) {
  printer_capabilities.page_output_quality =
      std::move(xps_capabilities.page_output_quality);
}

}  // namespace printing