OptionalContent.cc 8.38 KB
Newer Older
1
2
//========================================================================
//
3
// OptionalContent.cc
4
5
//
// Copyright 2007 Brad Hards <bradh@kde.org>
6
// Copyright 2008 Pino Toscano <pino@kde.org>
Albert Astals Cid's avatar
Albert Astals Cid committed
7
// Copyright 2008, 2010 Carlos Garcia Campos <carlosgc@gnome.org>
8
// Copyright 2008, 2010, 2011 Albert Astals Cid <aacid@kde.org>
9
// Copyright 2008 Mark Kaplan <mkaplan@finjan.com>
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// Released under the GPL (version 2, or later, at your option)
//
//========================================================================

#include <config.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "goo/gmem.h"
#include "goo/GooString.h"
#include "goo/GooList.h"
24
#include "Error.h"
25
26
27
28
29
30
// #include "PDFDocEncoding.h"
#include "OptionalContent.h"

//------------------------------------------------------------------------

OCGs::OCGs(Object *ocgObject, XRef *xref) :
31
  m_xref(xref)
32
33
{
  // we need to parse the dictionary here, and build optionalContentGroups
34
  ok = gTrue;
35
36
37
38
39
  optionalContentGroups = new GooList();

  Object ocgList;
  ocgObject->dictLookup("OCGs", &ocgList);
  if (!ocgList.isArray()) {
40
    error(-1, "Expected the optional content group list, but wasn't able to find it, or it isn't an Array");
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
41
    ocgList.free();
42
    ok = gFalse;
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
43
    return;
44
45
46
47
48
49
50
  }

  // we now enumerate over the ocgList, and build up the optionalContentGroups list.
  for(int i = 0; i < ocgList.arrayGetLength(); ++i) {
    Object ocg;
    ocgList.arrayGet(i, &ocg);
    if (!ocg.isDict()) {
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
51
      ocg.free();
52
53
      break;
    }
54
    OptionalContentGroup *thisOptionalContentGroup = new OptionalContentGroup(ocg.getDict());
55
56
57
58
59
60
61
62
63
64
65
66
67
    ocg.free();
    ocgList.arrayGetNF(i, &ocg);
    // TODO: we should create a lookup map from Ref to the OptionalContentGroup
    thisOptionalContentGroup->setRef( ocg.getRef() );
    ocg.free();
    // the default is ON - we change state later, depending on BaseState, ON and OFF
    thisOptionalContentGroup->setState(OptionalContentGroup::On);
    optionalContentGroups->append(thisOptionalContentGroup);
  }

  Object defaultOcgConfig;
  ocgObject->dictLookup("D", &defaultOcgConfig);
  if (!defaultOcgConfig.isDict()) {
68
    error(-1, "Expected the default config, but wasn't able to find it, or it isn't a Dictionary");
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
69
70
    defaultOcgConfig.free();
    ocgList.free();
71
    ok = gFalse;
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
72
    return;
73
  }
74

75
76
  Object baseState;
  defaultOcgConfig.dictLookup("BaseState", &baseState);
77
78
79
80
81
82
83
  if (baseState.isName("OFF")) {
    for (int i = 0; i < optionalContentGroups->getLength(); ++i) {
      OptionalContentGroup *group;

      group = (OptionalContentGroup *)optionalContentGroups->get(i);
      group->setState(OptionalContentGroup::Off);
    }
84
85
  }
  baseState.free();
86

87
88
89
90
91
92
93
94
95
  Object on;
  defaultOcgConfig.dictLookup("ON", &on);
  if (on.isArray()) {
    // ON is an optional element
    for (int i = 0; i < on.arrayGetLength(); ++i) {
      Object reference;
      on.arrayGetNF(i, &reference);
      if (!reference.isRef()) {
	// there can be null entries
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
96
	reference.free();
97
98
99
100
101
	break;
      }
      OptionalContentGroup *group = findOcgByRef( reference.getRef() );
      reference.free();
      if (!group) {
102
	error(-1, "Couldn't find group for reference");
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
	break;
      }
      group->setState(OptionalContentGroup::On);
    }
  }
  on.free();

  Object off;
  defaultOcgConfig.dictLookup("OFF", &off);
  if (off.isArray()) {
    // OFF is an optional element
    for (int i = 0; i < off.arrayGetLength(); ++i) {
      Object reference;
      off.arrayGetNF(i, &reference);
      if (!reference.isRef()) {
	// there can be null entries
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
119
	reference.free();
120
121
122
123
124
	break;
      }
      OptionalContentGroup *group = findOcgByRef( reference.getRef() );
      reference.free();
      if (!group) {
125
	error(-1, "Couldn't find group for reference to set OFF");
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
	break;
      }
      group->setState(OptionalContentGroup::Off);
    }
  }
  off.free();

  defaultOcgConfig.dictLookup("Order", &order);
  defaultOcgConfig.dictLookup("RBGroups", &rbgroups);

  ocgList.free();
  defaultOcgConfig.free();
}

OCGs::~OCGs()
{
142
  deleteGooList(optionalContentGroups, OptionalContentGroup);
143
144
  order.free();
  rbgroups.free();
145
146
147
148
149
150
151
152
153
154
155
156
}


bool OCGs::hasOCGs()
{
  return ( optionalContentGroups->getLength() > 0 );
}

OptionalContentGroup* OCGs::findOcgByRef( const Ref &ref)
{
  //TODO: make this more efficient
  OptionalContentGroup *ocg = NULL;
157
158
  for (int i=0; i < optionalContentGroups->getLength(); ++i) {
    ocg = (OptionalContentGroup*)optionalContentGroups->get(i);
159
    if ( (ocg->getRef().num == ref.num) && (ocg->getRef().gen == ref.gen) ) {
160
      return ocg;
161
162
    }
  }
163
164
165

  error(-1, "Could not find a OCG with Ref (%d:%d)", ref.num, ref.gen);

166
167
168
169
170
171
172
173
174
175
176
177
178
179
  // not found
  return NULL;
}

bool OCGs::optContentIsVisible( Object *dictRef )
{
  Object dictObj;
  Dict *dict;
  Object dictType;
  Object ocg;
  Object policy;
  bool result = true;
  dictRef->fetch( m_xref, &dictObj );
  if ( ! dictObj.isDict() ) {
180
    error(-1, "Unexpected oc reference target: %i", dictObj.getType() );
181
    dictObj.free();
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    return result;
  }
  dict = dictObj.getDict();
  // printf("checking if optContent is visible\n");
  dict->lookup("Type", &dictType);
  if (dictType.isName("OCMD")) {
    // If we supported Visibility Expressions, we'd check
    // for a VE entry, and then call out to the parser here...
    // printf("found OCMD dict\n");
    dict->lookup("P", &policy);
    dict->lookupNF("OCGs", &ocg);
    if (ocg.isArray()) {
      if (policy.isName("AllOn")) {
	result = allOn( ocg.getArray() );
      } else if (policy.isName("AllOff")) {
	result = allOff( ocg.getArray() );
      } else if (policy.isName("AnyOff")) {
	result = anyOff( ocg.getArray() );
      } else if ( (!policy.isName()) || (policy.isName("AnyOn") ) ) {
	// this is the default
	result = anyOn( ocg.getArray() );
      }
    } else if (ocg.isRef()) {
      OptionalContentGroup* oc = findOcgByRef( ocg.getRef() );      
206
      if ( oc && oc->getState() == OptionalContentGroup::Off ) {
207
208
209
210
211
212
213
214
215
	result = false;
      } else {
	result = true ;
      }
    }
    ocg.free();
    policy.free();
  } else if ( dictType.isName("OCG") ) {
    OptionalContentGroup* oc = findOcgByRef( dictRef->getRef() );
216
    if ( oc && oc->getState() == OptionalContentGroup::Off ) {
217
      result=false;
218
    }
219
  }
220
  dictType.free();
221
  dictObj.free();
222
223
224
225
226
227
228
229
230
231
232
  // printf("visibility: %s\n", result? "on" : "off");
  return result;
}

bool OCGs::allOn( Array *ocgArray )
{
  for (int i = 0; i < ocgArray->getLength(); ++i) {
    Object ocgItem;
    ocgArray->getNF(i, &ocgItem);
    if (ocgItem.isRef()) {
      OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
233
      if ( oc && oc->getState() == OptionalContentGroup::Off ) {
234
235
236
237
238
239
240
241
242
243
244
245
246
247
	return false;
      }
    }
  }
  return true;
}

bool OCGs::allOff( Array *ocgArray )
{
  for (int i = 0; i < ocgArray->getLength(); ++i) {
    Object ocgItem;
    ocgArray->getNF(i, &ocgItem);
    if (ocgItem.isRef()) {
      OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
248
      if ( oc && oc->getState() == OptionalContentGroup::On ) {
249
250
251
252
253
254
255
256
257
258
259
260
261
262
	return false;
      }
    }
  }
  return true;
}

bool OCGs::anyOn( Array *ocgArray )
{
  for (int i = 0; i < ocgArray->getLength(); ++i) {
    Object ocgItem;
    ocgArray->getNF(i, &ocgItem);
    if (ocgItem.isRef()) {
      OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
263
      if ( oc && oc->getState() == OptionalContentGroup::On ) {
264
265
266
267
268
269
270
271
272
273
274
275
276
277
	return true;
      }
    }
  }
  return false;
}

bool OCGs::anyOff( Array *ocgArray )
{
  for (int i = 0; i < ocgArray->getLength(); ++i) {
    Object ocgItem;
    ocgArray->getNF(i, &ocgItem);
    if (ocgItem.isRef()) {
      OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
278
      if ( oc && oc->getState() == OptionalContentGroup::Off ) {
279
280
281
282
283
284
285
286
287
	return true;
      }
    }
  }
  return false;
}

//------------------------------------------------------------------------

288
OptionalContentGroup::OptionalContentGroup(Dict *ocgDict) : m_name(NULL)
289
290
{
  Object ocgName;
291
  ocgDict->lookup("Name", &ocgName);
292
  if (!ocgName.isString()) {
293
    error(-1, "Expected the name of the OCG, but wasn't able to find it, or it isn't a String");
294
295
296
297
298
299
300
301
302
303
304
305
  } else {
    m_name = new GooString( ocgName.getString() );
  }
  ocgName.free();
}

OptionalContentGroup::OptionalContentGroup(GooString *label)
{
  m_name = label;
  m_state = On;
}

306
GooString* OptionalContentGroup::getName() const
307
308
309
310
311
312
313
314
315
{
  return m_name;
}

void OptionalContentGroup::setRef(const Ref ref)
{
  m_ref = ref;
}

316
Ref OptionalContentGroup::getRef() const
317
318
319
320
321
322
323
324
325
{
  return m_ref;
}

OptionalContentGroup::~OptionalContentGroup()
{
  delete m_name;
}