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(¶ms);
345 }
346 params = tmp2.dictLookup("BE");
347 if (params.isDict()) {
348 BE.parseMediaPlayParameters(¶ms);
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(¶ms);
362 }
363 params = tmp2.dictLookup("BE");
364 if (params.isDict()) {
365 BE.parseMediaScreenParameters(¶ms);
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