blob: 31ddb23abc0a5a504b1719852ae2ee9083334aaf [file] [log] [blame]
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001/*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <jpegrecoverymap/recoverymaputils.h>
18#include <jpegrecoverymap/recoverymap.h>
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000019#include <image_io/xml/xml_reader.h>
20#include <image_io/base/message_handler.h>
21#include <image_io/xml/xml_element_rules.h>
22#include <image_io/xml/xml_handler.h>
23#include <image_io/xml/xml_rule.h>
24
25#include <string>
26#include <sstream>
27
28using namespace photos_editing_formats::image_io;
29using namespace std;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000030
31namespace android::recoverymap {
32
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000033
34// Extremely simple XML Handler - just searches for interesting elements
35class XMPXmlHandler : public XmlHandler {
36public:
37
38 XMPXmlHandler() : XmlHandler() {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000039 gContainerItemState = NotStrarted;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000040 }
41
42 enum ParseState {
43 NotStrarted,
44 Started,
45 Done
46 };
47
48 virtual DataMatchResult StartElement(const XmlTokenContext& context) {
49 string val;
50 if (context.BuildTokenValue(&val)) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000051 if (!val.compare(gContainerItemName)) {
52 gContainerItemState = Started;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000053 } else {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000054 if (gContainerItemState != Done) {
55 gContainerItemState = NotStrarted;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000056 }
57 }
58 }
59 return context.GetResult();
60 }
61
62 virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000063 if (gContainerItemState == Started) {
64 gContainerItemState = Done;
65 lastAttributeName = "";
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000066 }
67 return context.GetResult();
68 }
69
Fyodor Kyslovad58a342022-12-12 02:10:35 +000070 virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000071 string val;
Fyodor Kyslovad58a342022-12-12 02:10:35 +000072 if (gContainerItemState == Started) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000073 if (context.BuildTokenValue(&val)) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000074 if (!val.compare(rangeScalingFactorAttrName)) {
75 lastAttributeName = rangeScalingFactorAttrName;
76 } else if (!val.compare(transferFunctionAttrName)) {
77 lastAttributeName = transferFunctionAttrName;
78 } else {
79 lastAttributeName = "";
80 }
81 }
82 }
83 return context.GetResult();
84 }
85
86 virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
87 string val;
88 if (gContainerItemState == Started) {
89 if (context.BuildTokenValue(&val, true)) {
90 if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
91 rangeScalingFactorStr = val;
92 } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
93 transferFunctionStr = val;
94 }
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000095 }
96 }
97 return context.GetResult();
98 }
99
100 bool getRangeScalingFactor(float* scaling_factor) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000101 if (gContainerItemState == Done) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000102 stringstream ss(rangeScalingFactorStr);
103 float val;
104 if (ss >> val) {
105 *scaling_factor = val;
106 return true;
107 } else {
108 return false;
109 }
110 } else {
111 return false;
112 }
113 }
114
115 bool getTransferFunction(jpegr_transfer_function* transfer_function) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000116 if (gContainerItemState == Done) {
117 stringstream ss(transferFunctionStr);
118 int val;
119 if (ss >> val) {
120 *transfer_function = static_cast<jpegr_transfer_function>(val);
121 return true;
122 } else {
123 return false;
124 }
125 } else {
126 return false;
127 }
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000128 return true;
129 }
130
131private:
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000132 static const string gContainerItemName;
133 static const string rangeScalingFactorAttrName;
134 static const string transferFunctionAttrName;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000135 string rangeScalingFactorStr;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000136 string transferFunctionStr;
137 string lastAttributeName;
138 ParseState gContainerItemState;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000139};
140
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000141const string XMPXmlHandler::gContainerItemName = "GContainer:Item";
142const string XMPXmlHandler::rangeScalingFactorAttrName = "RecoveryMap:RangeScalingFactor";
143const string XMPXmlHandler::transferFunctionAttrName = "RecoveryMap:TransferFunction";
144
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000145
146
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000147bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000148 string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
149
150 if (xmp_size < nameSpace.size()+2) {
151 // Data too short
152 return false;
153 }
154
155 if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
156 // Not correct namespace
157 return false;
158 }
159
160 // Position the pointers to the start of XMP XML portion
161 xmp_data += nameSpace.size()+1;
162 xmp_size -= nameSpace.size()+1;
163 XMPXmlHandler handler;
164
165 // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
166 while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
167 xmp_size--;
168 }
169
170 string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
171 MessageHandler msg_handler;
172 unique_ptr<XmlRule> rule(new XmlElementRule);
173 XmlReader reader(&handler, &msg_handler);
174 reader.StartParse(std::move(rule));
175 reader.Parse(str);
176 reader.FinishParse();
177 if (reader.HasErrors()) {
178 // Parse error
179 return false;
180 }
181
182 if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
183 return false;
184 }
185
186 if (!handler.getTransferFunction(&metadata->transferFunction)) {
187 return false;
188 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000189 return true;
190}
191
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000192} // namespace android::recoverymap