1 /*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
2  *
3  *  Use of this source code is governed by a BSD-style license that can
4  *  be found in the License.html file in the root of the source tree.
5  */
6 
7 //---------------------------------------------------------------------------
8 // Pre-compilation
9 #include "MediaInfo/PreComp.h"
10 #ifdef __BORLANDC__
11     #pragma hdrstop
12 #endif
13 //---------------------------------------------------------------------------
14 
15 //---------------------------------------------------------------------------
16 #include "MediaInfo/Setup.h"
17 //---------------------------------------------------------------------------
18 
19 //---------------------------------------------------------------------------
20 #if defined(MEDIAINFO_NISO_YES)
21 //---------------------------------------------------------------------------
22 
23 //---------------------------------------------------------------------------
24 #include "MediaInfo/Export/Export_Niso.h"
25 #include "MediaInfo/File__Analyse_Automatic.h"
26 #include "MediaInfo/OutputHelpers.h"
27 #include <ctime>
28 #include <cmath>
29 
30 using namespace std;
31 
32 //---------------------------------------------------------------------------
33 
34 namespace MediaInfoLib
35 {
36 //---------------------------------------------------------------------------
37 extern MediaInfo_Config Config;
38 //---------------------------------------------------------------------------
39 
40 //***************************************************************************
41 // Constructor/Destructor
42 //***************************************************************************
43 
44 //---------------------------------------------------------------------------
Export_Niso()45 Export_Niso::Export_Niso ()
46 {
47 }
48 
49 //---------------------------------------------------------------------------
~Export_Niso()50 Export_Niso::~Export_Niso ()
51 {
52 }
53 
54 //***************************************************************************
55 // Helpers
56 //***************************************************************************
57 
58 //---------------------------------------------------------------------------
Transform_Header()59 Node* Transform_Header()
60 {
61     //Root node
62     Node* Node_Header=new Node("mix:mix");
63     Node_Header->Add_Attribute("xmlns:mix", "http://www.loc.gov/mix/v20");
64     Node_Header->Add_Attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
65     Node_Header->Add_Attribute("xsi:schemaLocation", "http://www.loc.gov/mix/v20 http://www.loc.gov/standards/mix/mix20/mix20.xsd");
66 
67     return Node_Header;
68 }
69 
70 //---------------------------------------------------------------------------
ComputeSamplingFrequency(Node * Parent,Ztring & Value)71 void ComputeSamplingFrequency(Node* Parent, Ztring& Value)
72 {
73     while (Value.size()>0 && Value[Value.size()-1]==__T('0'))
74         Value.resize(Value.size()-1);
75     if (Value.size()>0 && Value[Value.size()-1]==__T('.'))
76         Value.resize(Value.size()-1);
77 
78     int32u SamplingFrequencyDenominator=0;
79     size_t Dot=Value.find(__T('.'));
80     if (Dot!=std::string::npos)
81     {
82         Value.erase(Dot, 1);
83         SamplingFrequencyDenominator=(int32u)std::pow((double)10, (double)Value.size()-Dot);
84     }
85 
86     Parent->Add_Child("mix:numerator", Value);
87 
88     if (SamplingFrequencyDenominator)
89         Parent->Add_Child("mix:denominator", Ztring().From_Number(SamplingFrequencyDenominator));
90 }
91 
92 //***************************************************************************
93 // Input
94 //***************************************************************************
95 
96 //---------------------------------------------------------------------------
Transform(MediaInfo_Internal & MI,Ztring ExternalMetadataValues,Ztring ExternalMetaDataConfig)97 Ztring Export_Niso::Transform(MediaInfo_Internal &MI, Ztring ExternalMetadataValues, Ztring ExternalMetaDataConfig)
98 {
99     bool UseExternalMetaData=(!ExternalMetaDataConfig.empty() && !ExternalMetadataValues.empty());
100 
101     Ztring ToReturn;
102     for (size_t Pos=0; Pos<MI.Count_Get(Stream_Image); Pos++)
103     {
104         Node* Node_Root=Transform_Header();
105         Node* Node_Extension=NULL;
106 
107         //Use external metadata
108         if (UseExternalMetaData)
109         {
110             Ztring FileName;
111             if (!MI.Get(Stream_General, 0, General_FileName).empty())
112                 FileName=MI.Get(Stream_General, 0, General_FileName);
113             if (!MI.Get(Stream_General, 0, General_FileExtension).empty())
114                 FileName+=__T('.')+MI.Get(Stream_General, 0, General_FileExtension);
115             if (FileName.empty())
116             {
117                 MediaInfoLib::Config.Log_Send(0xC0, 0xFF, 0, "File name not found in external metadata file");
118                 delete Node_Root;
119                 return Ztring();
120             }
121 
122             Node_Extension= new Node("mix:Extension");
123             Node* Node_CoreMain=NULL;
124 
125             if (ExternalMetaDataConfig.find(__T("<ebucore:ebuCoreMain"))<100)
126             {
127                 //TODO: merge with EBUCore code
128                 //Current date/time is ISO format
129                 time_t Seconds=time(NULL);
130                 Ztring DateTime; DateTime.Date_From_Seconds_1970((int32u)Seconds);
131                 if (DateTime.size() >= 4 && DateTime[0] == __T('U') && DateTime[1] == __T('T') && DateTime[2] == __T('C') && DateTime[3] == __T(' '))
132                 {
133                     DateTime.erase(0, 4);
134                     DateTime += __T('Z');
135                 }
136                 Ztring Date=DateTime.substr(0, 10);
137                 Ztring Time=DateTime.substr(11);
138 
139                 Node_CoreMain=Node_Extension->Add_Child("ebucore:ebuCoreMain");
140                 Node_CoreMain->Add_Attribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
141                 {
142                     Node_CoreMain->Add_Attribute("xmlns:ebucore", "urn:ebu:metadata-schema:ebucore");
143                     Node_CoreMain->Add_Attribute("xmlns:xalan", "http://xml.apache.org/xalan");
144                     Node_CoreMain->Add_Attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
145                     Node_CoreMain->Add_Attribute("xsi:schemaLocation", string("urn:ebu:metadata-schema:ebucore http") + string(MediaInfoLib::Config.Https_Get() ? "s" : "") + "://www.ebu.ch/metadata/schemas/EBUCore/20171009/ebucore.xsd");
146                     Node_CoreMain->Add_Attribute("version", "1.8");
147                     Node_CoreMain->Add_Attribute("writingLibraryName", "MediaInfoLib");
148                     Node_CoreMain->Add_Attribute("writingLibraryVersion", MediaInfoLib::Config.Info_Version_Get().SubString(__T(" - v"), Ztring()));
149                 }
150                 Node_CoreMain->Add_Attribute("dateLastModified", Date);
151                 Node_CoreMain->Add_Attribute("timeLastModified", Time);
152             }
153 
154             if (!ExternalMetadata(FileName, ExternalMetadataValues, ExternalMetaDataConfig,
155                                   ZtringList(__T("mix:Extension;ebucore:ebuCoreMain")), __T(""),
156                                   Node_CoreMain?Node_CoreMain:Node_Extension, NULL))
157             {
158                 delete Node_Root;
159                 return Ztring();
160             }
161         }
162 
163         //BasicDigitalObjectInformation
164         Node* Node_BasicDigitalObjectInformation=Node_Root->Add_Child("mix:BasicDigitalObjectInformation");
165         Node* Node_ObjectIdentifier=Node_BasicDigitalObjectInformation->Add_Child("mix:ObjectIdentifier");
166 
167         Node_ObjectIdentifier->Add_Child("mix:objectIdentifierType", std::string("MediaInfo"));
168         Node_ObjectIdentifier->Add_Child_IfNotEmpty(MI, Stream_General, 0, General_CompleteName, "mix:objectIdentifierValue");
169 
170         if (MI.Get(Stream_General, 0, General_FileSize).To_int64u()>0)
171             Node_BasicDigitalObjectInformation->Add_Child("mix:fileSize", MI.Get(Stream_General, 0, General_FileSize));
172 
173         if (MI.Get(Stream_General, 0, General_Format)==__T("TIFF"))
174              Node_BasicDigitalObjectInformation->Add_Child("mix:FormatDesignation")->Add_Child("mix:formatName", std::string("image/tiff"));
175 
176         std::string byteOrder=MI.Get(Stream_Image, Pos, Image_Format_Settings_Endianness).To_UTF8();
177         if (!byteOrder.empty())
178             Node_BasicDigitalObjectInformation->Add_Child("mix:byteOrder", byteOrder=="Little"?std::string("little endian"):(byteOrder=="Big"?std::string("big endian"): byteOrder));
179 
180         std::string CompressionScheme=MI.Get(Stream_Image, Pos, Image_Format).To_UTF8();
181         if (!CompressionScheme.empty())
182             Node_BasicDigitalObjectInformation->Add_Child("mix:Compression")->Add_Child("mix:compressionScheme", CompressionScheme=="Raw"?std::string("Uncompressed"):CompressionScheme);
183 
184         if (!MI.Get(Stream_Image, Pos, Image_Width).empty() ||
185             !MI.Get(Stream_Image, Pos, Image_Height).empty() ||
186             !MI.Get(Stream_Image, Pos, Image_ColorSpace).empty())
187         {
188             Node* Node_BasicImageCharacteristics=Node_Root->Add_Child("mix:BasicImageInformation")->Add_Child("mix:BasicImageCharacteristics");
189 
190             Node_BasicImageCharacteristics->Add_Child_IfNotEmpty(MI, Stream_Image, Pos, Image_Width, "mix:imageWidth");
191             Node_BasicImageCharacteristics->Add_Child_IfNotEmpty(MI, Stream_Image, Pos, Image_Height, "mix:imageHeight");
192 
193             //ReferenceBlackWhite
194             std::string ColorSpace=MI.Get(Stream_Image, Pos, Image_ColorSpace).To_UTF8();
195             if (!ColorSpace.empty())
196             {
197                 Node* Node_PhotometricInterpretation=Node_BasicImageCharacteristics->Add_Child("mix:PhotometricInterpretation");
198                 Node_PhotometricInterpretation->Add_Child("mix:colorSpace", ColorSpace);
199 
200                 Node* ReferenceBlackWhite=Node_PhotometricInterpretation->Add_Child("mix:ReferenceBlackWhite");
201                 for (size_t i=0; i < ColorSpace.size(); i++)
202                 {
203                     Node* Component=ReferenceBlackWhite->Add_Child("mix:Component");
204                     Component->Add_Child("mix:componentPhotometricInterpretation", string(1, ColorSpace[i]));
205                     Node* footroom=Component->Add_Child("mix:footroom");
206                     footroom->Add_Child("mix:numerator", Ztring::ToZtring(0));
207                     int8u BitDepth=MI.Get(Stream_Image, Pos, Image_BitDepth).To_int8u();
208                     if (BitDepth)
209                     {
210                         Node* headroom=Component->Add_Child("mix:headroom");
211                         headroom->Add_Child("mix:numerator", Ztring::ToZtring((1<<BitDepth)-1));
212                     }
213                 }
214             }
215         }
216 
217         //ImageCaptureMetadata
218         std::string Make=MI.Get(Stream_General, 0, General_Encoded_Application_CompanyName).To_UTF8();
219         std::string Model=MI.Get(Stream_General, 0, General_Encoded_Library_Name).To_UTF8();
220         std::string Software=MI.Get(Stream_General, 0, General_Encoded_Application_Name).To_UTF8();
221         std::string Encoded_Date=MI.Get(Stream_Image, Pos, Image_Encoded_Date).To_UTF8();
222         if (!Make.empty() || !Model.empty() || !Software.empty() || !Encoded_Date.empty())
223         {
224             Node* Node_ImageCaptureMetadata=Node_Root->Add_Child("mix:ImageCaptureMetadata");
225 
226             if (!Encoded_Date.empty())
227             {
228                 if (Encoded_Date.size()>4 && Encoded_Date[4]==':')
229                     Encoded_Date[4]='-';
230                 if (Encoded_Date.size()>7 && Encoded_Date[7]==':')
231                     Encoded_Date[7]='-';
232                 if (Encoded_Date.size()>10 && Encoded_Date[10]==' ')
233                     Encoded_Date[10]='T';
234                 Node* Node_GeneralCaptureInformation=Node_ImageCaptureMetadata->Add_Child("mix:GeneralCaptureInformation");
235                 Node_GeneralCaptureInformation->Add_Child("mix:dateTimeCreated", Encoded_Date);
236             }
237 
238             if (!Make.empty() || !Model.empty() || !Software.empty() )
239             {
240                 Node* Node_ScannerCapture=Node_ImageCaptureMetadata->Add_Child("mix:ScannerCapture");
241                 if (!Make.empty())
242                     Node_ScannerCapture->Add_Child("mix:scannerManufacturer", Make);
243                 if (!Model.empty())
244                     Node_ScannerCapture->Add_Child("mix:ScannerModel")->Add_Child("mix:scannerModelName", Model);
245                 if (!Software.empty())
246                     Node_ScannerCapture->Add_Child("mix:ScanningSystemSoftware")->Add_Child("mix:scanningSoftwareName", Software);
247             }
248         }
249 
250         if (!MI.Get(Stream_Image, Pos, __T("Density_X")).empty() ||
251             !MI.Get(Stream_Image, Pos, __T("Density_Y")).empty() ||
252             !MI.Get(Stream_Image, Pos, Image_ColorSpace).empty())
253         {
254             Node* Node_ImageAssessmentMetadata=Node_Root->Add_Child("mix:ImageAssessmentMetadata");
255 
256             //SpatialMetrics
257             string samplingFrequencyUnit=MI.Get(Stream_Image, Pos, __T("Density_Unit")).To_UTF8();
258             Ztring xSamplingFrequency=MI.Get(Stream_Image, Pos, __T("Density_X"));
259             Ztring ySamplingFrequency=MI.Get(Stream_Image, Pos, __T("Density_Y"));
260             if (!xSamplingFrequency.empty() || !ySamplingFrequency.empty())
261             {
262                 Node* Node_SpatialMetrics=Node_ImageAssessmentMetadata->Add_Child("mix:SpatialMetrics");
263 
264                 if (samplingFrequencyUnit.empty())
265                     Node_SpatialMetrics->Add_Child("mix:samplingFrequencyUnit", string("no absolute unit of measurement"));
266                 else if (samplingFrequencyUnit=="dpi")
267                     Node_SpatialMetrics->Add_Child("mix:samplingFrequencyUnit", string("in."));
268                 else if (samplingFrequencyUnit=="dpcm")
269                     Node_SpatialMetrics->Add_Child("mix:samplingFrequencyUnit", string("cm"));
270 
271                 if (!xSamplingFrequency.empty())
272                     ComputeSamplingFrequency(Node_SpatialMetrics->Add_Child("mix:xSamplingFrequency"), xSamplingFrequency);
273                 if (!ySamplingFrequency.empty())
274                     ComputeSamplingFrequency(Node_SpatialMetrics->Add_Child("mix:ySamplingFrequency"), ySamplingFrequency);
275             }
276 
277             size_t SamplesPerPixel=MI.Get(Stream_Image, Pos, Image_ColorSpace).length();
278             if (SamplesPerPixel)
279             {
280                 Node* Node_ImageColorEncoding=Node_ImageAssessmentMetadata->Add_Child("mix:ImageColorEncoding");
281 
282                 Node* Node_BitsPerSample=new Node("mix:BitsPerSample");
283                 for (size_t Pos2=0; Pos2<SamplesPerPixel; ++Pos2)
284                     Node_BitsPerSample->Add_Child_IfNotEmpty(MI, Stream_Image, Pos, Image_BitDepth, "mix:bitsPerSampleValue");
285 
286                 if (!Node_BitsPerSample->Childs.empty())
287                 {
288                     Node_BitsPerSample->Add_Child("mix:bitsPerSampleUnit", std::string("integer"));
289                     Node_ImageColorEncoding->Childs.push_back(Node_BitsPerSample);
290                 }
291                 else
292                    delete Node_BitsPerSample;
293 
294                 Node_ImageColorEncoding->Add_Child("mix:samplesPerPixel", Ztring().From_Number(SamplesPerPixel).To_UTF8());
295             }
296         }
297 
298         if (Node_Extension)
299             Node_Root->Childs.push_back(Node_Extension);
300 
301         ToReturn+=Ztring().From_UTF8(To_XML(*Node_Root, 0, true, true).c_str());
302     }
303 
304     if (ToReturn.empty())
305         ToReturn+=Ztring().From_UTF8(To_XML(*Transform_Header(), 0, true, true).c_str())+=__T("\n");
306 
307     //Carriage return
308     if (MediaInfoLib::Config.LineSeparator_Get()!=__T("\n"))
309         ToReturn.FindAndReplace(__T("\n"), MediaInfoLib::Config.LineSeparator_Get(), 0, Ztring_Recursive);
310 
311     return ToReturn;
312 }
313 
314 //***************************************************************************
315 //
316 //***************************************************************************
317 
318 } //NameSpace
319 
320 #endif
321