blob: 6475f63f69a7954ece98fed243e6425b99b6da93 [file] [log] [blame]
Fletcher Woodruffdd635822020-04-23 12:49:39 -06001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "xml_util.h"
6
7#include <memory>
8#include <utility>
9#include <string>
10
11#include <base/containers/flat_map.h>
12#include <libxml/tree.h>
13
14namespace {
15
16struct XmlNodeDeleter {
17 void operator()(xmlNode* p) { xmlFreeNode(p); }
18};
19using UniqueXmlNodePtr = std::unique_ptr<xmlNode, XmlNodeDeleter>;
20
21struct XmlDocDeleter {
22 void operator()(xmlDoc* p) { xmlFreeDoc(p); }
23};
24using UniqueXmlDocPtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
25
26constexpr char kPwgNs[] = "pwg";
27constexpr char kScanNs[] = "scan";
28using namespace_map = base::flat_map<std::string, xmlNs*>;
29
30inline const xmlChar* XmlCast(const char* p) {
31 return reinterpret_cast<const xmlChar*>(p);
32}
33
34// Serialize the xml tree in |root| into a vector. Consumes |root| and frees
35// its memory.
36std::vector<uint8_t> Serialize(UniqueXmlNodePtr root) {
37 UniqueXmlDocPtr doc(xmlNewDoc(XmlCast("1.0")));
38 // |doc| takes ownership of |root| here.
39 xmlDocSetRootElement(doc.get(), root.release());
40
41 xmlChar* buf;
42 int length;
43 xmlDocDumpFormatMemoryEnc(doc.get(), &buf, &length, "UTF-8", true);
44 std::vector<uint8_t> xml(buf, buf + length);
45 xmlFree(buf);
46
47 return xml;
48}
49
50// Adds a new node with namespace |ns| called |name| as a child of |node|.
51// Returns a non-owning pointer to the new node.
52xmlNode* AddNode(xmlNode* node, xmlNs* ns, const std::string& name) {
53 xmlNode* child = xmlNewNode(ns, XmlCast(name.c_str()));
54 xmlAddChild(node, child);
55 return child;
56}
57
58// Adds a new node with namespace |ns| called |name| and containing |content|
59// as a child of |node|.
60void AddNodeWithContent(xmlNode* node,
61 xmlNs* ns,
62 const std::string& name,
63 const std::string& content) {
64 xmlNode* child = xmlNewNode(ns, XmlCast(name.c_str()));
65 xmlNodeSetContent(child, XmlCast(content.c_str()));
66 xmlAddChild(node, child);
67}
68
69UniqueXmlNodePtr SourceCapabilitiesAsXml(const SourceCapabilities& caps,
70 const std::string& name,
71 namespace_map* namespaces) {
72 xmlNs* pwg = (*namespaces)[kPwgNs];
73 xmlNs* scan = (*namespaces)[kScanNs];
74 UniqueXmlNodePtr root(xmlNewNode(scan, XmlCast(name.c_str())));
75
76 AddNodeWithContent(root.get(), scan, "MinWidth", "16");
77 AddNodeWithContent(root.get(), scan, "MaxWidth", "2550");
78 AddNodeWithContent(root.get(), scan, "MinHeight", "16");
79 AddNodeWithContent(root.get(), scan, "MaxHeight", "3507");
80 AddNodeWithContent(root.get(), scan, "MaxScanRegions", "1");
81
82 xmlNode* profiles = AddNode(root.get(), scan, "SettingProfiles");
83 xmlNode* profile = AddNode(profiles, scan, "SettingProfile");
84 xmlNode* color_modes = AddNode(profile, scan, "ColorModes");
85 for (const std::string& mode : caps.color_modes) {
86 AddNodeWithContent(color_modes, scan, "ColorMode", mode);
87 }
88 xmlNode* document_formats = AddNode(profile, scan, "DocumentFormats");
89 for (const std::string& format : caps.formats) {
90 AddNodeWithContent(document_formats, pwg, "DocumentFormat", format);
91 }
92
93 xmlNode* resolutions = AddNode(profile, scan, "SupportedResolutions");
94 xmlNode* discrete_resolutions =
95 AddNode(resolutions, scan, "DiscreteResolutions");
96 for (int resolution : caps.resolutions) {
97 xmlNode* r = AddNode(discrete_resolutions, scan, "DiscreteResolution");
98 AddNodeWithContent(r, scan, "XResolution", std::to_string(resolution));
99 AddNodeWithContent(r, scan, "YResolution", std::to_string(resolution));
100 }
101
102 xmlNode* intents = AddNode(root.get(), scan, "SupportedIntents");
103 AddNodeWithContent(intents, scan, "Intent", "Document");
104 AddNodeWithContent(intents, scan, "Intent", "TextAndGraphic");
105 AddNodeWithContent(intents, scan, "Intent", "Photo");
106 AddNodeWithContent(intents, scan, "Intent", "Preview");
107
108 AddNodeWithContent(root.get(), scan, "MaxOpticalXResolution", "2400");
109 AddNodeWithContent(root.get(), scan, "MaxOpticalYResolution", "2400");
110 AddNodeWithContent(root.get(), scan, "RiskyLeftMargin", "0");
111 AddNodeWithContent(root.get(), scan, "RiskyRightMargin", "0");
112 AddNodeWithContent(root.get(), scan, "RiskyTopMargin", "0");
113 AddNodeWithContent(root.get(), scan, "RiskyBottomMargin", "0");
114
115 return root;
116}
117
118} // namespace
119
120std::vector<uint8_t> ScannerCapabilitiesAsXml(const ScannerCapabilities& caps) {
121 UniqueXmlNodePtr root(xmlNewNode(nullptr, XmlCast("ScannerCapabilities")));
122
123 xmlNs* pwg =
124 xmlNewNs(root.get(), XmlCast("http://www.pwg.org/schemas/2010/12/sm"),
125 XmlCast("pwg"));
126 xmlNs* scan = xmlNewNs(
127 root.get(), XmlCast("http://schemas.hp.com/imaging/escl/2011/05/03"),
128 XmlCast("scan"));
129 xmlNewNs(root.get(), XmlCast("http://www.w3.org/2001/XMLSchema-instance"),
130 XmlCast("xsi"));
131 xmlSetNs(root.get(), scan);
132
133 namespace_map namespaces;
134 namespaces[kPwgNs] = pwg;
135 namespaces[kScanNs] = scan;
136
137 AddNodeWithContent(root.get(), pwg, "Version", "2.63");
138 AddNodeWithContent(root.get(), pwg, "MakeAndModel", caps.make_and_model);
139 AddNodeWithContent(root.get(), pwg, "SerialNumber", caps.serial_number);
140
141 xmlNode* platen = AddNode(root.get(), scan, "Platen");
142 UniqueXmlNodePtr platen_caps = SourceCapabilitiesAsXml(
143 caps.platen_capabilities, "PlatenInputCaps", &namespaces);
144 xmlAddChild(platen, platen_caps.release());
145
146 return Serialize(std::move(root));
147}