1 //*********************************************************************************
2 //                               Rendition.cc
3 //---------------------------------------------------------------------------------
4 //
5 //---------------------------------------------------------------------------------
6 // Hugo Mercier <hmercier31[at]gmail.com> (c) 2008
7 // Pino Toscano <pino@kde.org> (c) 2008
8 // Carlos Garcia Campos <carlosgc@gnome.org> (c) 2010
9 // Tobias Koenig <tobias.koenig@kdab.com> (c) 2012
10 // Albert Astals Cid <aacid@kde.org> (C) 2017, 2018
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 //*********************************************************************************
26 
27 #include <cmath>
28 #include "Rendition.h"
29 #include "FileSpec.h"
30 
MediaWindowParameters()31 MediaWindowParameters::MediaWindowParameters()
32 {
33     // default values
34     type = windowEmbedded;
35     width = -1;
36     height = -1;
37     relativeTo = windowRelativeToDocument;
38     XPosition = 0.5;
39     YPosition = 0.5;
40     hasTitleBar = true;
41     hasCloseButton = true;
42     isResizeable = true;
43 }
44 
~MediaWindowParameters()45 MediaWindowParameters::~MediaWindowParameters() { }
46 
parseFWParams(Object * obj)47 void MediaWindowParameters::parseFWParams(Object *obj)
48 {
49     Object tmp = obj->dictLookup("D");
50     if (tmp.isArray()) {
51         Array *dim = tmp.getArray();
52 
53         if (dim->getLength() >= 2) {
54             Object dd = dim->get(0);
55             if (dd.isInt()) {
56                 width = dd.getInt();
57             }
58 
59             dd = dim->get(1);
60             if (dd.isInt()) {
61                 height = dd.getInt();
62             }
63         }
64     }
65 
66     tmp = obj->dictLookup("RT");
67     if (tmp.isInt()) {
68         int t = tmp.getInt();
69         switch (t) {
70         case 0:
71             relativeTo = windowRelativeToDocument;
72             break;
73         case 1:
74             relativeTo = windowRelativeToApplication;
75             break;
76         case 2:
77             relativeTo = windowRelativeToDesktop;
78             break;
79         }
80     }
81 
82     tmp = obj->dictLookup("P");
83     if (tmp.isInt()) {
84         int t = tmp.getInt();
85 
86         switch (t) {
87         case 0: // Upper left
88             XPosition = 0.0;
89             YPosition = 0.0;
90             break;
91         case 1: // Upper Center
92             XPosition = 0.5;
93             YPosition = 0.0;
94             break;
95         case 2: // Upper Right
96             XPosition = 1.0;
97             YPosition = 0.0;
98             break;
99         case 3: // Center Left
100             XPosition = 0.0;
101             YPosition = 0.5;
102             break;
103         case 4: // Center
104             XPosition = 0.5;
105             YPosition = 0.5;
106             break;
107         case 5: // Center Right
108             XPosition = 1.0;
109             YPosition = 0.5;
110             break;
111         case 6: // Lower Left
112             XPosition = 0.0;
113             YPosition = 1.0;
114             break;
115         case 7: // Lower Center
116             XPosition = 0.5;
117             YPosition = 1.0;
118             break;
119         case 8: // Lower Right
120             XPosition = 1.0;
121             YPosition = 1.0;
122             break;
123         }
124     }
125 
126     tmp = obj->dictLookup("T");
127     if (tmp.isBool()) {
128         hasTitleBar = tmp.getBool();
129     }
130     tmp = obj->dictLookup("UC");
131     if (tmp.isBool()) {
132         hasCloseButton = tmp.getBool();
133     }
134     tmp = obj->dictLookup("R");
135     if (tmp.isInt()) {
136         isResizeable = (tmp.getInt() != 0);
137     }
138 }
139 
MediaParameters()140 MediaParameters::MediaParameters()
141 {
142     // instanciate to default values
143 
144     volume = 100;
145     fittingPolicy = fittingUndefined;
146     autoPlay = true;
147     repeatCount = 1.0;
148     opacity = 1.0;
149     showControls = false;
150     duration = 0;
151 }
152 
~MediaParameters()153 MediaParameters::~MediaParameters() { }
154 
parseMediaPlayParameters(Object * obj)155 void MediaParameters::parseMediaPlayParameters(Object *obj)
156 {
157     Object tmp = obj->dictLookup("V");
158     if (tmp.isInt()) {
159         volume = tmp.getInt();
160     }
161 
162     tmp = obj->dictLookup("C");
163     if (tmp.isBool()) {
164         showControls = tmp.getBool();
165     }
166 
167     tmp = obj->dictLookup("F");
168     if (tmp.isInt()) {
169         int t = tmp.getInt();
170 
171         switch (t) {
172         case 0:
173             fittingPolicy = fittingMeet;
174             break;
175         case 1:
176             fittingPolicy = fittingSlice;
177             break;
178         case 2:
179             fittingPolicy = fittingFill;
180             break;
181         case 3:
182             fittingPolicy = fittingScroll;
183             break;
184         case 4:
185             fittingPolicy = fittingHidden;
186             break;
187         case 5:
188             fittingPolicy = fittingUndefined;
189             break;
190         }
191     }
192 
193     // duration parsing
194     // duration's default value is set to 0, which means : intrinsinc media duration
195     tmp = obj->dictLookup("D");
196     if (tmp.isDict()) {
197         Object oname = tmp.dictLookup("S");
198         if (oname.isName()) {
199             const char *name = oname.getName();
200             if (!strcmp(name, "F"))
201                 duration = -1; // infinity
202             else if (!strcmp(name, "T")) {
203                 Object ddict = tmp.dictLookup("T");
204                 if (ddict.isDict()) {
205                     Object tmp2 = ddict.dictLookup("V");
206                     if (tmp2.isNum()) {
207                         duration = (unsigned long)(tmp2.getNum());
208                     }
209                 }
210             }
211         }
212     }
213 
214     tmp = obj->dictLookup("A");
215     if (tmp.isBool()) {
216         autoPlay = tmp.getBool();
217     }
218 
219     tmp = obj->dictLookup("RC");
220     if (tmp.isNum()) {
221         repeatCount = tmp.getNum();
222     }
223 }
224 
parseMediaScreenParameters(Object * obj)225 void MediaParameters::parseMediaScreenParameters(Object *obj)
226 {
227     Object tmp = obj->dictLookup("W");
228     if (tmp.isInt()) {
229         int t = tmp.getInt();
230 
231         switch (t) {
232         case 0:
233             windowParams.type = MediaWindowParameters::windowFloating;
234             break;
235         case 1:
236             windowParams.type = MediaWindowParameters::windowFullscreen;
237             break;
238         case 2:
239             windowParams.type = MediaWindowParameters::windowHidden;
240             break;
241         case 3:
242             windowParams.type = MediaWindowParameters::windowEmbedded;
243             break;
244         }
245     }
246 
247     // background color
248     tmp = obj->dictLookup("B");
249     if (tmp.isArray()) {
250         Array *color = tmp.getArray();
251 
252         Object component = color->get(0);
253         bgColor.r = component.getNum();
254 
255         component = color->get(1);
256         bgColor.g = component.getNum();
257 
258         component = color->get(2);
259         bgColor.b = component.getNum();
260     }
261 
262     // opacity
263     tmp = obj->dictLookup("O");
264     if (tmp.isNum()) {
265         opacity = tmp.getNum();
266     }
267 
268     if (windowParams.type == MediaWindowParameters::windowFloating) {
269         Object winDict = obj->dictLookup("F");
270         if (winDict.isDict()) {
271             windowParams.parseFWParams(&winDict);
272         }
273     }
274 }
275 
~MediaRendition()276 MediaRendition::~MediaRendition()
277 {
278     delete fileName;
279     delete contentType;
280 }
281 
MediaRendition(Object * obj)282 MediaRendition::MediaRendition(Object *obj)
283 {
284     bool hasClip = false;
285 
286     ok = true;
287     fileName = nullptr;
288     contentType = nullptr;
289     isEmbedded = false;
290 
291     //
292     // Parse media clip data
293     //
294     Object tmp2 = obj->dictLookup("C");
295     if (tmp2.isDict()) { // media clip
296         hasClip = true;
297         Object tmp = tmp2.dictLookup("S");
298         if (tmp.isName()) {
299             if (!strcmp(tmp.getName(), "MCD")) { // media clip data
300                 Object obj1 = tmp2.dictLookup("D");
301                 if (obj1.isDict()) {
302                     Object obj2 = obj1.dictLookup("F");
303                     if (obj2.isString()) {
304                         fileName = obj2.getString()->copy();
305                     }
306                     obj2 = obj1.dictLookup("EF");
307                     if (obj2.isDict()) {
308                         Object embedded = obj2.dictLookup("F");
309                         if (embedded.isStream()) {
310                             isEmbedded = true;
311                             embeddedStreamObject = embedded.copy();
312                         }
313                     }
314 
315                     // TODO: D might be a form XObject too
316                 } else {
317                     error(errSyntaxError, -1, "Invalid Media Clip Data");
318                     ok = false;
319                 }
320 
321                 // FIXME: ignore CT if D is a form XObject
322                 obj1 = tmp2.dictLookup("CT");
323                 if (obj1.isString()) {
324                     contentType = obj1.getString()->copy();
325                 }
326             } else if (!strcmp(tmp.getName(), "MCS")) { // media clip data
327                 // TODO
328             }
329         } else {
330             error(errSyntaxError, -1, "Invalid Media Clip");
331             ok = false;
332         }
333     }
334 
335     if (!ok)
336         return;
337 
338     //
339     // parse Media Play Parameters
340     tmp2 = obj->dictLookup("P");
341     if (tmp2.isDict()) { // media play parameters
342         Object params = tmp2.dictLookup("MH");
343         if (params.isDict()) {
344             MH.parseMediaPlayParameters(&params);
345         }
346         params = tmp2.dictLookup("BE");
347         if (params.isDict()) {
348             BE.parseMediaPlayParameters(&params);
349         }
350     } else if (!hasClip) {
351         error(errSyntaxError, -1, "Invalid Media Rendition");
352         ok = false;
353     }
354 
355     //
356     // parse Media Screen Parameters
357     tmp2 = obj->dictLookup("SP");
358     if (tmp2.isDict()) { // media screen parameters
359         Object params = tmp2.dictLookup("MH");
360         if (params.isDict()) {
361             MH.parseMediaScreenParameters(&params);
362         }
363         params = tmp2.dictLookup("BE");
364         if (params.isDict()) {
365             BE.parseMediaScreenParameters(&params);
366         }
367     }
368 }
369 
MediaRendition(const MediaRendition & other)370 MediaRendition::MediaRendition(const MediaRendition &other)
371 {
372     ok = other.ok;
373     MH = other.MH;
374     BE = other.BE;
375     isEmbedded = other.isEmbedded;
376     embeddedStreamObject = other.embeddedStreamObject.copy();
377 
378     if (other.contentType)
379         contentType = other.contentType->copy();
380     else
381         contentType = nullptr;
382 
383     if (other.fileName)
384         fileName = other.fileName->copy();
385     else
386         fileName = nullptr;
387 }
388 
outputToFile(FILE * fp)389 void MediaRendition::outputToFile(FILE *fp)
390 {
391     if (!isEmbedded)
392         return;
393 
394     embeddedStreamObject.streamReset();
395 
396     while (true) {
397         int c = embeddedStreamObject.streamGetChar();
398         if (c == EOF)
399             break;
400 
401         fwrite(&c, 1, 1, fp);
402     }
403 }
404 
copy() const405 MediaRendition *MediaRendition::copy() const
406 {
407     return new MediaRendition(*this);
408 }
409 
410 // TODO: SelectorRendition
411