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
31MediaWindowParameters::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
45MediaWindowParameters::~MediaWindowParameters() { }
46
47void MediaWindowParameters::parseFWParams(Object *obj)
48{
49 Object tmp = obj->dictLookup(key: "D");
50 if (tmp.isArray()) {
51 Array *dim = tmp.getArray();
52
53 if (dim->getLength() >= 2) {
54 Object dd = dim->get(i: 0);
55 if (dd.isInt()) {
56 width = dd.getInt();
57 }
58
59 dd = dim->get(i: 1);
60 if (dd.isInt()) {
61 height = dd.getInt();
62 }
63 }
64 }
65
66 tmp = obj->dictLookup(key: "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(key: "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(key: "T");
127 if (tmp.isBool()) {
128 hasTitleBar = tmp.getBool();
129 }
130 tmp = obj->dictLookup(key: "UC");
131 if (tmp.isBool()) {
132 hasCloseButton = tmp.getBool();
133 }
134 tmp = obj->dictLookup(key: "R");
135 if (tmp.isInt()) {
136 isResizeable = (tmp.getInt() != 0);
137 }
138}
139
140MediaParameters::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
153MediaParameters::~MediaParameters() { }
154
155void MediaParameters::parseMediaPlayParameters(Object *obj)
156{
157 Object tmp = obj->dictLookup(key: "V");
158 if (tmp.isInt()) {
159 volume = tmp.getInt();
160 }
161
162 tmp = obj->dictLookup(key: "C");
163 if (tmp.isBool()) {
164 showControls = tmp.getBool();
165 }
166
167 tmp = obj->dictLookup(key: "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(key: "D");
196 if (tmp.isDict()) {
197 Object oname = tmp.dictLookup(key: "S");
198 if (oname.isName()) {
199 const char *name = oname.getName();
200 if (!strcmp(s1: name, s2: "F")) {
201 duration = -1; // infinity
202 } else if (!strcmp(s1: name, s2: "T")) {
203 Object ddict = tmp.dictLookup(key: "T");
204 if (ddict.isDict()) {
205 Object tmp2 = ddict.dictLookup(key: "V");
206 if (tmp2.isNum()) {
207 duration = (unsigned long)(tmp2.getNum());
208 }
209 }
210 }
211 }
212 }
213
214 tmp = obj->dictLookup(key: "A");
215 if (tmp.isBool()) {
216 autoPlay = tmp.getBool();
217 }
218
219 tmp = obj->dictLookup(key: "RC");
220 if (tmp.isNum()) {
221 repeatCount = tmp.getNum();
222 }
223}
224
225void MediaParameters::parseMediaScreenParameters(Object *obj)
226{
227 Object tmp = obj->dictLookup(key: "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(key: "B");
249 if (tmp.isArray()) {
250 Array *color = tmp.getArray();
251
252 Object component = color->get(i: 0);
253 bgColor.r = component.getNum();
254
255 component = color->get(i: 1);
256 bgColor.g = component.getNum();
257
258 component = color->get(i: 2);
259 bgColor.b = component.getNum();
260 }
261
262 // opacity
263 tmp = obj->dictLookup(key: "O");
264 if (tmp.isNum()) {
265 opacity = tmp.getNum();
266 }
267
268 if (windowParams.type == MediaWindowParameters::windowFloating) {
269 Object winDict = obj->dictLookup(key: "F");
270 if (winDict.isDict()) {
271 windowParams.parseFWParams(obj: &winDict);
272 }
273 }
274}
275
276MediaRendition::~MediaRendition()
277{
278 delete fileName;
279 delete contentType;
280}
281
282MediaRendition::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(key: "C");
295 if (tmp2.isDict()) { // media clip
296 hasClip = true;
297 Object tmp = tmp2.dictLookup(key: "S");
298 if (tmp.isName()) {
299 if (!strcmp(s1: tmp.getName(), s2: "MCD")) { // media clip data
300 Object obj1 = tmp2.dictLookup(key: "D");
301 if (obj1.isDict()) {
302 Object obj2 = obj1.dictLookup(key: "F");
303 if (obj2.isString()) {
304 fileName = obj2.getString()->copy();
305 }
306 obj2 = obj1.dictLookup(key: "EF");
307 if (obj2.isDict()) {
308 Object embedded = obj2.dictLookup(key: "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(category: errSyntaxError, pos: -1, msg: "Invalid Media Clip Data");
318 ok = false;
319 }
320
321 // FIXME: ignore CT if D is a form XObject
322 obj1 = tmp2.dictLookup(key: "CT");
323 if (obj1.isString()) {
324 contentType = obj1.getString()->copy();
325 }
326 } else if (!strcmp(s1: tmp.getName(), s2: "MCS")) { // media clip data
327 // TODO
328 }
329 } else {
330 error(category: errSyntaxError, pos: -1, msg: "Invalid Media Clip");
331 ok = false;
332 }
333 }
334
335 if (!ok) {
336 return;
337 }
338
339 //
340 // parse Media Play Parameters
341 tmp2 = obj->dictLookup(key: "P");
342 if (tmp2.isDict()) { // media play parameters
343 Object params = tmp2.dictLookup(key: "MH");
344 if (params.isDict()) {
345 MH.parseMediaPlayParameters(obj: &params);
346 }
347 params = tmp2.dictLookup(key: "BE");
348 if (params.isDict()) {
349 BE.parseMediaPlayParameters(obj: &params);
350 }
351 } else if (!hasClip) {
352 error(category: errSyntaxError, pos: -1, msg: "Invalid Media Rendition");
353 ok = false;
354 }
355
356 //
357 // parse Media Screen Parameters
358 tmp2 = obj->dictLookup(key: "SP");
359 if (tmp2.isDict()) { // media screen parameters
360 Object params = tmp2.dictLookup(key: "MH");
361 if (params.isDict()) {
362 MH.parseMediaScreenParameters(obj: &params);
363 }
364 params = tmp2.dictLookup(key: "BE");
365 if (params.isDict()) {
366 BE.parseMediaScreenParameters(obj: &params);
367 }
368 }
369}
370
371MediaRendition::MediaRendition(const MediaRendition &other)
372{
373 ok = other.ok;
374 MH = other.MH;
375 BE = other.BE;
376 isEmbedded = other.isEmbedded;
377 embeddedStreamObject = other.embeddedStreamObject.copy();
378
379 if (other.contentType) {
380 contentType = other.contentType->copy();
381 } else {
382 contentType = nullptr;
383 }
384
385 if (other.fileName) {
386 fileName = other.fileName->copy();
387 } else {
388 fileName = nullptr;
389 }
390}
391
392void MediaRendition::outputToFile(FILE *fp)
393{
394 if (!isEmbedded) {
395 return;
396 }
397
398 embeddedStreamObject.streamReset();
399
400 while (true) {
401 int c = embeddedStreamObject.streamGetChar();
402 if (c == EOF) {
403 break;
404 }
405
406 fwrite(ptr: &c, size: 1, n: 1, s: fp);
407 }
408}
409
410MediaRendition *MediaRendition::copy() const
411{
412 return new MediaRendition(*this);
413}
414
415// TODO: SelectorRendition
416

source code of poppler/poppler/Rendition.cc