pdfedit  0.4.5
About: PDFedit is a free and open source library for manipulating PDF documents.
  Fossies Dox: pdfedit-0.4.5.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
cpagecontents.cc
Go to the documentation of this file.
1 /*
2  * PDFedit - free program for PDF document manipulation.
3  * Copyright (C) 2006-2009 PDFedit team: Michal Hocko,
4  * Jozef Misutka,
5  * Martin Petricek
6  * Former team members: Miroslav Jahoda
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program (in doc/LICENSE.GPL); if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307 USA
21  *
22  * Project is hosted on http://sourceforge.net/projects/pdfedit
23  */
24 // vim:tabstop=4:shiftwidth=4:noexpandtab:textwidth=80
25 
26 // static
27 #include "kernel/static.h"
28 
29 #include "kernel/cpagecontents.h"
30 #include "kernel/factories.h"
31 #include "utils/observer.h"
32 #include "kernel/cobject.h"
33 #include "kernel/cpage.h"
34 #include "kernel/cpdf.h"
35 #include "kernel/cpagedisplay.h"
37 #include "kernel/cinlineimage.h"
38 
39 //==========================================================
40 namespace pdfobjects {
41 //==========================================================
42 
43 using namespace std;
44 using namespace boost;
45 using namespace observer;
46 using namespace utils;
47 
48 
49 //==========================================================
50 // Contents watchdog
51 //==========================================================
52 
53 
54 //
55 //
56 //
57 void
58 CPageContents::ContentsWatchDog::notify (shared_ptr<IProperty> newValue,
59  shared_ptr<const IProperty::ObserverContext> context) const throw()
60 {
61  kernelPrintDbg (debug::DBG_DBG, "context type=" << context->getType());
62 
63  //Several scenarios can happen
64  //1) page dictionary gets changed
65  // OK 1.1 - added Contents entry
66  // OK 1.2 - removed Contents entry
67  //2) Contents entry
68  // OK 2.1 - changed stream
69  // 2.2 - changed array
70  // OK 2.2.1 -- added entry
71  // OK 2.2.2 -- removed entry
72  // OK 2.2.3 -- changed entry
73  //
74 
75  // Switch type
76  switch(context->getType())
77  {
78  // 2.2 Stream was changed - not supported for now on
79  // 2.2.3 -- changed entry
81  break;
82 
83  // All other possibilities
85  {
86  // Is it a dictionary Page dictionary
87  shared_ptr<const CDict::CDictComplexObserverContext> ctxtdict =
88  dynamic_pointer_cast<const CDict::CDictComplexObserverContext,
89  const IChangeContext<IProperty> > (context);
90  if (ctxtdict)
91  {
92  //
93  //Several scenarios can happen
94  //1) page dictionary gets changed
95  // 1.1 - added Contents entry
96  // 1.2 - removed Contents entry
97  //
98  // if it is not about Contents do nothing
99  if (Specification::Page::CONTENTS != ctxtdict->getValueId())
100  return;
101 
102  // 1.2 Contents entry was removed
103  if (isNull(newValue))
104  {
105  shared_ptr<IProperty> oldValue = ctxtdict->getOriginalValue();
106  // Unregister observer
107  _cnt->unreg_observer (oldValue);
108 
109  // 1.1 Contents entry was added
110  }else
111  {
112  _cnt->reg_observer(newValue);
113  }
114 
115  break;
116  }
117 
118  // Is it an array (Contents) -- do nothing just reparse
119  shared_ptr<const CArray::CArrayComplexObserverContext> ctxtarray =
120  dynamic_pointer_cast<const CArray::CArrayComplexObserverContext,
121  const IChangeContext<IProperty> > (context);
122  if (ctxtarray)
123  {
124  //
125  //2) Contents entry
126  // 2.2 - changed array
127  // 2.2.1 -- added entry
128  // 2.2.2 -- removed entry
129  //
130  break;
131  }
132 
133  }
134 
135  default:
136  assert (!"Invalid change context - contents observer!");
137  break;
138  }
139 
140  // Parse content streams (add or delete of object)
141  try {
142  _cnt->parse ();
143  }catch (...)
144  {
145  _cnt->_page->invalidate ();
146  }
147 }
148 
149 
150 
151 //==========================================================
152 namespace {
153 //==========================================================
154 
155 
160  template<typename Container>
161  boost::shared_ptr<CStream>
162  createStreamFromObjects (const Container& cont, boost::weak_ptr<CPdf> pdf)
163  {
164  // Create stream with one default property Length
165  shared_ptr<CStream> newstr (new CStream());
166 
167  // Insert our change tag
168  std::string str;
169  {
170  std::string tmpop;
171  ContentsChangeTag::create()->getStringRepresentation (tmpop);
172  str += tmpop + " ";
173  }
174 
175  //
176  // Get string representation of new content stream
177  //
178  typename Container::const_iterator it = cont.begin();
179  for (; it != cont.end(); ++it)
180  {
181  std::string tmpop;
182  (*it)->getStringRepresentation (tmpop);
183  str += tmpop + " ";
184  }
186 
187  // Set the stream
188  newstr->setBuffer (str);
189 
190  // Set ref and indiref reserve free indiref for the new object
191  boost::shared_ptr<CPdf> p = pdf.lock();
192  assert(p);
193  IndiRef newref = p->addIndirectProperty (newstr);
194  newstr = IProperty::getSmartCObjectPtr<CStream> (p->getIndirectProperty (newref));
195  assert (newstr);
196 
197  return newstr;
198  }
199 
200 
204  template<typename In, typename Out>
205  void getAllCStreams (const In& in, Out& out)
206  {
207  for (typename In::const_iterator it = in.begin(); it != in.end(); ++it)
208  {
209  Out tmp;
210  (*it)->getCStreams (tmp);
211  copy (tmp.begin(), tmp.end(), back_inserter (out));
212  }
213  }
214 
215 //==========================================================
216 } // namespace
217 //==========================================================
218 
219 
220 //==========================================================
221 // CPageContents
222 //==========================================================
223 
224 CPageContents::CPageContents (CPage* page) : _page(page), _wd (new ContentsWatchDog (this))
225 {
226  if (_page)
227  _dict = _page->getDictionary();
228  reg_observer ();
229 }
230 
232 {
233  reset ();
234 }
235 
236 
237 shared_ptr<CContentStream>
239 {
240  init();
241  for (CCs::iterator it = _ccs.begin(); it != _ccs.end(); ++it)
242  if ((*it).get() == cc)
243  return *it;
244 
245  assert (!"Contentstream not found");
246  throw CObjInvalidOperation ();
247 }
248 
249 
250 shared_ptr<CContentStream>
252 {
253  init();
254  if (pos >= _ccs.size())
255  throw CObjInvalidOperation ();
256  return _ccs[pos];
257 }
258 
259 
260 //
261 //
262 //
263 template<typename Container>
264 void
265 CPageContents::addToFront (const Container& cont)
266 {
267  // Create cstream from container of pdf operators
268  shared_ptr<CStream> stream = createStreamFromObjects (cont, _dict->getPdf());
269  assert (hasValidRef (stream)); assert (hasValidPdf (stream));
270  if (!hasValidPdf(stream) || !hasValidPdf(stream))
271  throw CObjInvalidObject ();
272 
273  // Change the contents entry
274  CRef rf (stream->getIndiRef());
275  toFront (rf);
276 
277  // Parse new stream to content stream and add it to the streams
278  CContentStream::CStreams streams;
279  streams.push_back (stream);
280  boost::shared_ptr<GfxResources> res;
281  boost::shared_ptr<GfxState> state;
282  _xpdf_display_params (res, state);
283  CCs _tmp;
284  // Init cc and save smart pointer of the content stream so pdfoperators can get it
285  boost::shared_ptr<CContentStream> cc (new CContentStream(streams,state,res));
286  cc->setSmartPointer (cc);
287 
288  // copy it to front
289  _tmp.push_back (cc);
290  init();
291  std::copy (_ccs.begin(), _ccs.end(), std::back_inserter(_tmp));
292  _ccs = _tmp;
293 
294  // Indicate change
295  change ();
296 }
297 template void CPageContents::addToFront<vector<shared_ptr<PdfOperator> > > (const vector<shared_ptr<PdfOperator> >& cont);
298 template void CPageContents::addToFront<deque<shared_ptr<PdfOperator> > > (const deque<shared_ptr<PdfOperator> >& cont);
299 
300 //
301 //
302 //
303 template<typename Container>
304 void
305 CPageContents::addToBack (const Container& cont)
306 {
307  // Create cstream from container of pdf operators
308  if (!hasValidPdf(_dict))
309  throw CObjInvalidObject ();
310  shared_ptr<CStream> stream = createStreamFromObjects (cont, _dict->getPdf());
311  assert (hasValidRef (stream)); assert (hasValidPdf (stream));
312  if (!hasValidPdf(stream) || !hasValidPdf(stream))
313  throw CObjInvalidObject ();
314 
315  // Change the contents entry
316  CRef rf (stream->getIndiRef());
317  toBack (rf);
318 
319 
320  // Parse new stream to content stream and add it to the streams
321  CContentStream::CStreams streams;
322  streams.push_back (stream);
323  boost::shared_ptr<GfxResources> res;
324  boost::shared_ptr<GfxState> state;
325  _xpdf_display_params (res, state);
326  // Init and save smart pointer
327  boost::shared_ptr<CContentStream> cc (new CContentStream(streams,state,res));
328  cc->setSmartPointer (cc);
329  init();
330  _ccs.push_back (cc);
331 
332  // Indicate change
333  change ();
334 }
335 template void CPageContents::addToBack<vector<shared_ptr<PdfOperator> > > (const vector<shared_ptr<PdfOperator> >& cont);
336 template void CPageContents::addToBack<deque<shared_ptr<PdfOperator> > > (const deque<shared_ptr<PdfOperator> >& cont);
337 
338 
339 //
340 //
341 //
342 void
343 CPageContents::remove (size_t csnum)
344 {
345  if (!hasValidPdf(_dict))
346  throw CObjInvalidObject ();
347  init();
348  if (csnum >= _ccs.size())
349  throw OutOfRange ();
350 
351  // Change the contents entry
352  remove (_ccs[csnum]);
353  // Remove contentstream from container
354  _ccs.erase (_ccs.begin() + csnum, _ccs.begin() + csnum + 1);
355 
356  // Indicate change
357  change ();
358 }
359 
360 //
361 //
362 //
363 void
364 CPageContents::getText (std::string& text, const string* encoding, const libs::Rectangle* rc) const
365 {
367 
368  // Create text output device
369  boost::scoped_ptr<TextOutputDev> textDev (new ::TextOutputDev (NULL, gFalse, gFalse, gFalse));
370  if (!textDev->isOk())
371  throw CObjInvalidOperation ();
372 
373  // Display page
374  _page->display()->displayPage (*textDev);
375 
376  // Set encoding
377  if (encoding)
378  globalParams->setTextEncoding(const_cast<char*>(encoding->c_str()));
379 
380  // Get the text
381  libs::Rectangle rec = (rc)? *rc : _page->display()->getPageRect();
382  scoped_ptr<GString> gtxt (textDev->getText(rec.xleft, rec.yleft, rec.xright, rec.yright));
383  text = gtxt->getCString();
384 }
385 
386 
387 //
388 // Text search/find
389 //
390 
391 //
392 // Find all occcurences of a text on a page
393 //
394 template<typename RectangleContainer>
395 size_t CPageContents::findText (std::string text,
396  RectangleContainer& recs,
397  const TextSearchParams&) const
398 {
399  // Create text output device
400  scoped_ptr<TextOutputDev> textDev (new ::TextOutputDev (NULL, gFalse, gFalse, gFalse));
401  assert (textDev->isOk());
402  if (!textDev->isOk())
403  throw CObjInvalidOperation ();
404 
405  // Get the text
406  _page->display()->displayPage (*textDev);
407 
408  GBool startAtTop, stopAtBottom, startAtLast, stopAtLast, caseSensitive, backward;
409  startAtTop = stopAtBottom = startAtLast = stopAtLast = gTrue;
410  caseSensitive = backward = gFalse;
411 
412  double xMin = 0, yMin = 0, xMax = 0, yMax = 0;
413 
414  // Convert text to unicode (slightly modified from from PDFCore.cc)
415  int length = static_cast<int>(text.length());
416  ::Unicode* utext = static_cast<Unicode*> (new Unicode [length]);
417  for (int i = 0; i < length; ++i)
418  utext[i] = static_cast<Unicode> (text[i] & 0xff);
419 
420  if (textDev->findText(utext, length, startAtTop, stopAtBottom,
421  startAtLast,stopAtLast, caseSensitive, backward,
422  &xMin, &yMin, &xMax, &yMax))
423  {
424  startAtTop = gFalse;
425 
426  recs.push_back (libs::Rectangle (xMin, yMin, xMax, yMax));
427  // Get all text objects
428  while (textDev->findText (utext, length,
429  startAtTop, stopAtBottom,
430  startAtLast, stopAtLast,
431  caseSensitive, backward,
432  &xMin, &yMin, &xMax, &yMax))
433  {
434  recs.push_back (libs::Rectangle (xMin, yMin, xMax, yMax));
435  }
436  }
437 
438  //
439  // Find out the words...
440  //
441  delete[] utext;
442  return recs.size();
443 }
444 
445 // Explicit instantiation
446 template size_t CPageContents::findText<std::vector<libs::Rectangle> >
447  (std::string text,
448  std::vector<libs::Rectangle>& recs,
449  const TextSearchParams& params) const;
450 
451 //
452 //
453 //
454 void
455 CPageContents::replaceText (const std::string& what, const std::string& with)
456 {
457  init();
458  // Get the objects with specific comparator
459  for (CCs::iterator it = _ccs.begin (); it != _ccs.end(); ++it)
460  (*it)->replaceText (what, with);
461 }
462 
463 //
464 //
465 //
466 void
467 CPageContents::addText (const std::string& what,
468  const libs::Point& where,
469  const std::string& font_id)
470 {
471  init();
472 
473  // create the array of PDF operators and add them to back of content streams
474  // q
475  // BT
476  // rg col
477  // fname fsize Tf
478  // x y Td
479  // text Tj
480  // ET
481  // Q
482  //
483  std::string fontName (font_id);
484  if (fontName.empty())
485  fontName = "PDFEDIT_F1";
486  double fontSize = 15.0;
487  shared_ptr<UnknownCompositePdfOperator> q(new UnknownCompositePdfOperator("q", "Q"));
488  shared_ptr<UnknownCompositePdfOperator> BT(new UnknownCompositePdfOperator("BT", "ET"));
489  PdfOperator::Operands fontOperands;
490  fontOperands.push_back(shared_ptr<IProperty>(new CName (fontName)) );
491  fontOperands.push_back(shared_ptr<IProperty>(new CReal (fontSize)));
492  q->push_back(BT,q);
493  BT->push_back(createOperator("Tf", fontOperands), getLastOperator(BT));
494 
495  _likely_tm.set_position(where);
496  PdfOperator::Operands posOperands = _likely_tm;
497  BT->push_back(createOperator("Tm", posOperands), getLastOperator(BT));
498 
499  PdfOperator::Operands textOperands;
500  textOperands.push_back(shared_ptr<IProperty>(new CString (what)));
501  BT->push_back(createOperator("Tj", textOperands), getLastOperator(BT));
502  PdfOperator::Operands emptyOperands;
503  BT->push_back(createOperator("ET", emptyOperands), getLastOperator(BT));
504  q->push_back(createOperator("Q", emptyOperands), getLastOperator(q));
505 
506  std::vector<shared_ptr<PdfOperator> > contents;
507  contents.push_back(q);
508 
509  addToBack (contents);
510 }
511 
512 void
514  const libs::Point& image_size,
515  const libs::Point& where)
516 {
517  init();
518 
519  // create the array of PDF operators and add them to back of content streams
520  // q
521  // BI % Begin inline image object
522  // /W 17 % Width in samples
523  // /H 17 % Height in samples
524  // /CS /RGB % Color space
525  // /BPC 8 % Bits per component
526  // /F [/A85 /LZW] % Filters
527  // ID % Begin image data
528  // J1/gKA>.]AN&J?]-<HW]aRVcg*bb.\eKAdVV%/PcZ
529  // …Omitted data… // R.s(4KE3&d&7hb*7[%Ct2HCqC~> // EI // Q // shared_ptr<UnknownCompositePdfOperator> q(new UnknownCompositePdfOperator("q", "Q")); // translate q->push_back(createOperatorTranslation(where.x, where.y), getLastOperator(q)); // scale q->push_back(createOperatorScale(image_size.x, image_size.y), getLastOperator(q)); CDict image_dict; image_dict.addProperty ("W", CInt (image_size.x)); image_dict.addProperty ("H", CInt (image_size.y)); image_dict.addProperty ("CS", CName ("RGB")); image_dict.addProperty ("BPC", CInt (8)); CInlineImage img (image_dict, what); shared_ptr<CInlineImage> inline_image (new CInlineImage (image_dict, what)); shared_ptr<InlineImageCompositePdfOperator> BI(new InlineImageCompositePdfOperator (inline_image)); q->push_back(BI,getLastOperator(q)); PdfOperator::Operands o; q->push_back(createOperator("Q", o), getLastOperator(q)); std::vector<shared_ptr<PdfOperator> > contents; contents.push_back(q); addToBack (contents); } //========================================================== namespace { //========================================================== struct ToFront { static inline void add (CArray& arr, CRef& ref, IProperty& content) { arr.addProperty (ref); arr.addProperty (content); } static inline void add (CArray& arr, CRef& ref) { arr.addProperty (0, ref); } }; struct ToBack { static inline void add (CArray& arr, CRef& ref, IProperty& content) { arr.addProperty (content); arr.addProperty (ref); } static inline void add (CArray& arr, CRef& ref) { arr.addProperty (arr.getPropertyCount(), ref); } }; enum OPERWHERE {FRONT, BACK} ; template<int T> struct OpTrait; template<> struct OpTrait<FRONT> {typedef struct ToFront Oper; }; template<> struct OpTrait<BACK> {typedef struct ToBack Oper; }; // addSomewhere template<OPERWHERE WHERE> void cc_add (shared_ptr<CDict> _dict, CRef& ref) { // contents not present if (!_dict->containsProperty (Specification::Page::CONTENTS)) { CArray arr; arr.addProperty (ref); _dict->addProperty (Specification::Page::CONTENTS, arr); // contents present }else { shared_ptr<IProperty> content = _dict->getProperty (Specification::Page::CONTENTS); shared_ptr<IProperty> realcontent = getReferencedObject(content); assert (content); // Contents can be either stream or an array of streams if (isStream (realcontent)) { CArray arr; OpTrait<WHERE>::Oper::add (arr, ref, *content); _dict->setProperty (Specification::Page::CONTENTS, arr); }else if (isArray (realcontent)) { // We can be sure that streams are indirect objects (pdf spec) shared_ptr<CArray> array = IProperty::getSmartCObjectPtr<CArray> (realcontent); OpTrait<WHERE>::Oper::add (*array, ref); }else // Neither stream nor array { kernelPrintDbg (debug::DBG_ERR, "Content stream type: " << realcontent->getType()); throw ElementBadTypeException ("Bad content stream type."); } } } //========================================================== } // namespace //========================================================== // // // void CPageContents::toFront (CRef& ref) { { ContentsObserverFreeSection reg_lock (this); cc_add<FRONT> (_dict, ref); } // Indicate change change (); } // // // void CPageContents::toBack (CRef& ref) { { ContentsObserverFreeSection reg_lock (this); cc_add<BACK> (_dict, ref); } // Indicate change change (); } // // // /** * Set Contents entry from a container of content streams. * Indicats that the page changed. */ template<typename Cont> void CPageContents::setContents (shared_ptr<CDict> dict, const Cont& cont) { if (dict->containsProperty (Specification::Page::CONTENTS)) dict->delProperty (Specification::Page::CONTENTS); // // Loop throug all content streams and add all cstreams from each // content streams to Contents entry of page dictionary // typedef vector<shared_ptr<CStream> > Css; Css css; getAllCStreams (cont, css); // Loop through all cstreams for (Css::iterator it = css.begin(); it != css.end(); ++it) { assert (hasValidPdf (*it)); assert (hasValidRef (*it)); if (!hasValidPdf (*it) || !hasValidRef (*it)) throw XpdfInvalidObject (); CRef rf ((*it)->getIndiRef ()); cc_add<BACK> (dict, rf); } } // Explicit instantiation template void CPageContents::setContents<vector<shared_ptr<CContentStream> > > (shared_ptr<CDict> dict, const vector<shared_ptr<CContentStream> >& cont); // // // void CPageContents::remove (shared_ptr<const CContentStream> cs) { if (!_dict->containsProperty (Specification::Page::CONTENTS)) throw CObjInvalidOperation (); { ContentsObserverFreeSection reg_lock (this); // // Loop throug all content streams and add all cstreams from each // content streams to Contents entry of page dictionary // typedef vector<shared_ptr<CStream> > Css; Css css; cs->getCStreams (css); // Loop through all cstreams for (Css::iterator it = css.begin(); it != css.end(); ++it) { assert (hasValidPdf (*it)); assert (hasValidRef (*it)); if (!hasValidPdf (*it) || !hasValidRef (*it)) throw CObjInvalidObject (); // Remove the reference remove ((*it)->getIndiRef ()); } } // Indicate change change (); } // // // void CPageContents::remove (const IndiRef& rf) { shared_ptr<IProperty> content = _dict->getProperty (Specification::Page::CONTENTS); shared_ptr<IProperty> realcontent = getReferencedObject (content); assert (content); // Contents can be either stream or an array of streams if (isStream (realcontent)) { // Set empty contents CArray arr; _dict->setProperty (Specification::Page::CONTENTS, arr); }else if (isArray (realcontent)) { // We can be sure that streams are indirect objects (pdf spec) shared_ptr<CArray> array = IProperty::getSmartCObjectPtr<CArray> (realcontent); for (size_t i = 0; i < array->getPropertyCount(); ++i) { IndiRef _rf = getRefFromArray (array,i); if (_rf == rf) { array->delProperty (i); return; } } }else // Neither stream nor array { kernelPrintDbg (debug::DBG_ERR, "Content stream type: " << realcontent->getType()); throw ElementBadTypeException ("Bad content stream type."); } } // // // void CPageContents::reparse ( ) { if (!hasValidPdf(_dict) || !hasValidRef(_dict)) throw CObjInvalidObject (); // // Create state and resources // boost::shared_ptr<GfxResources> res; boost::shared_ptr<GfxState> state; _xpdf_display_params (res, state); // Set only bboxes // TODO: is there any possible way that _ccs is not initialized yet? // can someone call reparse sooner than parse? for (CCs::iterator it = _ccs.begin(); it != _ccs.end(); ++it) (*it)->reparse (true, state, res); change (); } bool CPageContents::parse () { if (!hasValidPdf(_dict) || !hasValidRef(_dict)) throw CObjInvalidObject (); // Clear content streams _ccs.clear(); // // Create state and resources // boost::shared_ptr<GfxResources> res; boost::shared_ptr<GfxState> state; _xpdf_display_params (res, state); // // Get the stream representing content stream (if any), make an xpdf object // and finally instantiate CContentStream // if (!_dict->containsProperty (Specification::Page::CONTENTS)) return true; shared_ptr<IProperty> contents = getReferencedObject (_dict->getProperty (Specification::Page::CONTENTS)); assert (contents); CContentStream::CStreams streams; // // Contents can be either stream or an array of streams // if (isStream (contents)) { shared_ptr<CStream> stream = IProperty::getSmartCObjectPtr<CStream> (contents); streams.push_back (stream); }else if (isArray (contents)) { // We can be sure that streams are indirect objects (pdf spec) shared_ptr<CArray> array = IProperty::getSmartCObjectPtr<CArray> (contents); for (size_t i = 0; i < array->getPropertyCount(); ++i) streams.push_back (getCStreamFromArray(array,i)); }else // Neither stream nor array { kernelPrintDbg (debug::DBG_ERR, "Content stream type: " << contents->getType()); throw ElementBadTypeException ("Bad content stream type."); } // // Create content streams, each cycle will take one/more content streams from streams variable // assert (_ccs.empty()); // True if Contents is not [ ] while (!streams.empty()) { shared_ptr<CContentStream> cc (new CContentStream(streams,state,res)); // Save smart pointer of the content stream so pdfoperators can return it cc->setSmartPointer (cc); _ccs.push_back (cc); } // uff, go through the first content operators and find out usefull information // - for now the text orientation // - pdfoperators will be reimplemented anyway for (CCs::const_iterator it = _ccs.begin(); it != _ccs.end(); ++it) { CContentStream::Operators ops; (*it)->getPdfOperators (ops); if (ops.empty()) continue; PdfOperator::Iterator opit = PdfOperator::getIterator (ops.front()); while (!opit.isEnd()) { std::string tmp; opit.getCurrent()->getOperatorName (tmp); PdfOperator::Operands operands; if (tmp == "Tm") { kernelPrintDbg (debug::DBG_WARN, "Using non default different Tm"); opit.getCurrent()->getParameters (operands); _likely_tm = operands; } opit.next(); } } // Indicate change change (); // Everything went ok return true; } void CPageContents::reg_observer (boost::shared_ptr<IProperty> ip) const { if (ip) { REGISTER_SHAREDPTR_OBSERVER(ip, _wd); }else { // Register dictionary and Contents observer REGISTER_SHAREDPTR_OBSERVER(_dict, _wd); // If it contains Contents register observer on it too if (_dict->containsProperty(Specification::Page::CONTENTS)) { shared_ptr<IProperty> prop = _dict->getProperty(Specification::Page::CONTENTS); REGISTER_SHAREDPTR_OBSERVER(prop, _wd); } } } // // // void CPageContents::unreg_observer (boost::shared_ptr<IProperty> ip) const { if (ip) { UNREGISTER_SHAREDPTR_OBSERVER(ip, _wd); }else { // Unregister dictionary observer UNREGISTER_SHAREDPTR_OBSERVER(_dict, _wd); // Unregister contents observer if (_dict->containsProperty(Specification::Page::CONTENTS)) { shared_ptr<IProperty> prop = _dict->getProperty(Specification::Page::CONTENTS); UNREGISTER_SHAREDPTR_OBSERVER(prop, _wd); } } } void CPageContents::change (bool invalid) { _page->_objectChanged (invalid); } void CPageContents::_xpdf_display_params (boost::shared_ptr<GfxResources>& res, boost::shared_ptr<GfxState>& state) { _page->display()->createXpdfDisplayParams (res, state); } size_t CPageContents::_page_pos () const { return _page->getPagePosition(); } // // // void CPageContents::moveAbove (shared_ptr<const CContentStream> ct) { // Get the next item init(); CCs::iterator itNext = find (_ccs.begin(), _ccs.end(), ct); if (itNext == _ccs.end()) throw CObjInvalidOperation (); ++itNext; if (itNext == _ccs.end()) throw OutOfRange (); // Delete next item but store it shared_ptr<CContentStream> tmp = *itNext; _ccs.erase (itNext, itNext + 1); // Insert stored item before supplied (simply swap ct with the next item) _ccs.insert (find (_ccs.begin(), _ccs.end(), ct), tmp); { ContentsObserverFreeSection reg_lock (this); setContents (_dict, _ccs); } // Also change Contents entry of page dictionary // Indicate change change (); } // // // void CPageContents::moveBelow (shared_ptr<const CContentStream> ct) { // Get the item index unsigned int pos = 0; init(); for (pos = 0; pos < _ccs.size(); ++pos) if (_ccs[pos] == ct) break; // If first or not found throw exception if (pos == _ccs.size() || 0 == pos) throw CObjInvalidOperation (); // Swap shared_ptr<CContentStream> tmp = _ccs[pos]; _ccs[pos] = _ccs[pos - 1]; _ccs[pos - 1] = tmp; // Also change Contents entry of page dictionary { ContentsObserverFreeSection reg_lock (this); setContents (_dict, _ccs); } // Also change Contents entry of page dictionary // Indicate change change (); } // // // void CPageContents::moveAbove (size_t pos) { moveAbove (_page->contents()->getContentStream (pos)); } // // // void CPageContents::moveBelow (size_t pos) { moveBelow (_page->contents()->getContentStream (pos)); } void CPageContents::reset () { // we already made a reset if (!_page) return; unreg_observer (); _page = NULL; _dict.reset (); _wd.reset (); assert (!_wd.use_count()); } //========================================================== } // namespace pdfobjects //==========================================================
530  // R.s(4KE3&d&7hb*7[%Ct2HCqC~>
531  // EI
532  // Q
533  //
534  shared_ptr<UnknownCompositePdfOperator> q(new UnknownCompositePdfOperator("q", "Q"));
535 
536  // translate
537  q->push_back(createOperatorTranslation(where.x, where.y), getLastOperator(q));
538  // scale
539  q->push_back(createOperatorScale(image_size.x, image_size.y), getLastOperator(q));
540 
541 
542  CDict image_dict;
543  image_dict.addProperty ("W", CInt (image_size.x));
544  image_dict.addProperty ("H", CInt (image_size.y));
545  image_dict.addProperty ("CS", CName ("RGB"));
546  image_dict.addProperty ("BPC", CInt (8));
547  CInlineImage img (image_dict, what);
548  shared_ptr<CInlineImage> inline_image (new CInlineImage (image_dict, what));
549  shared_ptr<InlineImageCompositePdfOperator> BI(new InlineImageCompositePdfOperator (inline_image));
550 
551  q->push_back(BI,getLastOperator(q));
553  q->push_back(createOperator("Q", o), getLastOperator(q));
554 
555  std::vector<shared_ptr<PdfOperator> > contents;
556  contents.push_back(q);
557 
558  addToBack (contents);
559 }
560 
561 
562 
563 
564 //==========================================================
565 namespace {
566 //==========================================================
567 
568 
569  struct ToFront
570  {
571  static inline void add (CArray& arr, CRef& ref, IProperty& content)
572  {
573  arr.addProperty (ref);
574  arr.addProperty (content);
575  }
576  static inline void add (CArray& arr, CRef& ref)
577  { arr.addProperty (0, ref); }
578  };
579  struct ToBack
580  {
581  static inline void add (CArray& arr, CRef& ref, IProperty& content)
582  {
583  arr.addProperty (content);
584  arr.addProperty (ref);
585  }
586  static inline void add (CArray& arr, CRef& ref)
587  { arr.addProperty (arr.getPropertyCount(), ref); }
588  };
589  enum OPERWHERE {FRONT, BACK} ;
590  template<int T> struct OpTrait;
591  template<> struct OpTrait<FRONT> {typedef struct ToFront Oper; };
592  template<> struct OpTrait<BACK> {typedef struct ToBack Oper; };
593 
594 
595  // addSomewhere
596  template<OPERWHERE WHERE>
597  void
598  cc_add (shared_ptr<CDict> _dict, CRef& ref)
599  {
600  // contents not present
601  if (!_dict->containsProperty (Specification::Page::CONTENTS))
602  {
603  CArray arr;
604  arr.addProperty (ref);
605  _dict->addProperty (Specification::Page::CONTENTS, arr);
606 
607  // contents present
608  }else
609  {
610  shared_ptr<IProperty> content = _dict->getProperty (Specification::Page::CONTENTS);
611  shared_ptr<IProperty> realcontent = getReferencedObject(content);
612  assert (content);
613  // Contents can be either stream or an array of streams
614  if (isStream (realcontent))
615  {
616  CArray arr;
617  OpTrait<WHERE>::Oper::add (arr, ref, *content);
618  _dict->setProperty (Specification::Page::CONTENTS, arr);
619 
620  }else if (isArray (realcontent))
621  {
622  // We can be sure that streams are indirect objects (pdf spec)
623  shared_ptr<CArray> array = IProperty::getSmartCObjectPtr<CArray> (realcontent);
624  OpTrait<WHERE>::Oper::add (*array, ref);
625 
626  }else // Neither stream nor array
627  {
628  kernelPrintDbg (debug::DBG_ERR, "Content stream type: " << realcontent->getType());
629  throw ElementBadTypeException ("Bad content stream type.");
630  }
631  }
632  }
633 
634 //==========================================================
635 } // namespace
636 //==========================================================
637 
638 //
639 //
640 //
641 void
642 CPageContents::toFront (CRef& ref)
643 {
644  {
645  ContentsObserverFreeSection reg_lock (this);
646  cc_add<FRONT> (_dict, ref);
647  }
648  // Indicate change
649  change ();
650 }
651 
652 //
653 //
654 //
655 void
656 CPageContents::toBack (CRef& ref)
657 {
658  {
659  ContentsObserverFreeSection reg_lock (this);
660  cc_add<BACK> (_dict, ref);
661  }
662  // Indicate change
663  change ();
664 }
665 
666 
667 //
668 //
669 //
674 template<typename Cont>
675 void CPageContents::setContents (shared_ptr<CDict> dict, const Cont& cont)
676 {
677 
678  if (dict->containsProperty (Specification::Page::CONTENTS))
679  dict->delProperty (Specification::Page::CONTENTS);
680 
681  //
682  // Loop throug all content streams and add all cstreams from each
683  // content streams to Contents entry of page dictionary
684  //
685  typedef vector<shared_ptr<CStream> > Css;
686  Css css;
687  getAllCStreams (cont, css);
688 
689  // Loop through all cstreams
690  for (Css::iterator it = css.begin(); it != css.end(); ++it)
691  {
692  assert (hasValidPdf (*it));
693  assert (hasValidRef (*it));
694  if (!hasValidPdf (*it) || !hasValidRef (*it))
695  throw XpdfInvalidObject ();
696  CRef rf ((*it)->getIndiRef ());
697  cc_add<BACK> (dict, rf);
698  }
699 }
700 // Explicit instantiation
701 template void CPageContents::setContents<vector<shared_ptr<CContentStream> > >
702  (shared_ptr<CDict> dict, const vector<shared_ptr<CContentStream> >& cont);
703 
704 //
705 //
706 //
707 void
708 CPageContents::remove (shared_ptr<const CContentStream> cs)
709 {
710  if (!_dict->containsProperty (Specification::Page::CONTENTS))
711  throw CObjInvalidOperation ();
712 
713  {
714  ContentsObserverFreeSection reg_lock (this);
715 
716  //
717  // Loop throug all content streams and add all cstreams from each
718  // content streams to Contents entry of page dictionary
719  //
720  typedef vector<shared_ptr<CStream> > Css;
721  Css css;
722  cs->getCStreams (css);
723 
724  // Loop through all cstreams
725  for (Css::iterator it = css.begin(); it != css.end(); ++it)
726  {
727  assert (hasValidPdf (*it));
728  assert (hasValidRef (*it));
729  if (!hasValidPdf (*it) || !hasValidRef (*it))
730  throw CObjInvalidObject ();
731 
732  // Remove the reference
733  remove ((*it)->getIndiRef ());
734  }
735  }
736  // Indicate change
737  change ();
738 }
739 
740 //
741 //
742 //
743 void
744 CPageContents::remove (const IndiRef& rf)
745 {
746  shared_ptr<IProperty> content = _dict->getProperty (Specification::Page::CONTENTS);
747  shared_ptr<IProperty> realcontent = getReferencedObject (content);
748  assert (content);
749  // Contents can be either stream or an array of streams
750  if (isStream (realcontent))
751  {
752  // Set empty contents
753  CArray arr;
754  _dict->setProperty (Specification::Page::CONTENTS, arr);
755 
756  }else if (isArray (realcontent))
757  {
758  // We can be sure that streams are indirect objects (pdf spec)
759  shared_ptr<CArray> array = IProperty::getSmartCObjectPtr<CArray> (realcontent);
760  for (size_t i = 0; i < array->getPropertyCount(); ++i)
761  {
762  IndiRef _rf = getRefFromArray (array,i);
763  if (_rf == rf)
764  {
765  array->delProperty (i);
766  return;
767  }
768  }
769 
770  }else // Neither stream nor array
771  {
772  kernelPrintDbg (debug::DBG_ERR, "Content stream type: " << realcontent->getType());
773  throw ElementBadTypeException ("Bad content stream type.");
774  }
775 }
776 
777 //
778 //
779 //
780 void
782 {
783  if (!hasValidPdf(_dict) || !hasValidRef(_dict))
784  throw CObjInvalidObject ();
785 
786  //
787  // Create state and resources
788  //
789  boost::shared_ptr<GfxResources> res;
790  boost::shared_ptr<GfxState> state;
791  _xpdf_display_params (res, state);
792 
793  // Set only bboxes
794  // TODO: is there any possible way that _ccs is not initialized yet?
795  // can someone call reparse sooner than parse?
796  for (CCs::iterator it = _ccs.begin(); it != _ccs.end(); ++it)
797  (*it)->reparse (true, state, res);
798 
799  change ();
800 }
801 
802 bool
803 CPageContents::parse ()
804 {
805  if (!hasValidPdf(_dict) || !hasValidRef(_dict))
806  throw CObjInvalidObject ();
807 
808  // Clear content streams
809  _ccs.clear();
810 
811  //
812  // Create state and resources
813  //
814  boost::shared_ptr<GfxResources> res;
815  boost::shared_ptr<GfxState> state;
816  _xpdf_display_params (res, state);
817 
818  //
819  // Get the stream representing content stream (if any), make an xpdf object
820  // and finally instantiate CContentStream
821  //
822  if (!_dict->containsProperty (Specification::Page::CONTENTS))
823  return true;
824  shared_ptr<IProperty> contents = getReferencedObject (_dict->getProperty (Specification::Page::CONTENTS));
825  assert (contents);
826 
827  CContentStream::CStreams streams;
828 
829  //
830  // Contents can be either stream or an array of streams
831  //
832  if (isStream (contents))
833  {
834  shared_ptr<CStream> stream = IProperty::getSmartCObjectPtr<CStream> (contents);
835  streams.push_back (stream);
836 
837  }else if (isArray (contents))
838  {
839  // We can be sure that streams are indirect objects (pdf spec)
840  shared_ptr<CArray> array = IProperty::getSmartCObjectPtr<CArray> (contents);
841  for (size_t i = 0; i < array->getPropertyCount(); ++i)
842  streams.push_back (getCStreamFromArray(array,i));
843 
844  }else // Neither stream nor array
845  {
846  kernelPrintDbg (debug::DBG_ERR, "Content stream type: " << contents->getType());
847  throw ElementBadTypeException ("Bad content stream type.");
848  }
849 
850  //
851  // Create content streams, each cycle will take one/more content streams from streams variable
852  //
853  assert (_ccs.empty());
854  // True if Contents is not [ ]
855  while (!streams.empty())
856  {
857  shared_ptr<CContentStream> cc (new CContentStream(streams,state,res));
858  // Save smart pointer of the content stream so pdfoperators can return it
859  cc->setSmartPointer (cc);
860  _ccs.push_back (cc);
861  }
862 
863  // uff, go through the first content operators and find out usefull information
864  // - for now the text orientation
865  // - pdfoperators will be reimplemented anyway
866  for (CCs::const_iterator it = _ccs.begin();
867  it != _ccs.end();
868  ++it)
869  {
871  (*it)->getPdfOperators (ops);
872  if (ops.empty())
873  continue;
874  PdfOperator::Iterator opit = PdfOperator::getIterator (ops.front());
875  while (!opit.isEnd())
876  {
877  std::string tmp;
878  opit.getCurrent()->getOperatorName (tmp);
879  PdfOperator::Operands operands;
880  if (tmp == "Tm")
881  {
882  kernelPrintDbg (debug::DBG_WARN, "Using non default different Tm");
883  opit.getCurrent()->getParameters (operands);
884  _likely_tm = operands;
885  }
886  opit.next();
887  }
888  }
889 
890 
891 
892  // Indicate change
893  change ();
894 
895  // Everything went ok
896  return true;
897 }
898 
899 
900 
901 void
902 CPageContents::reg_observer (boost::shared_ptr<IProperty> ip) const
903 {
904  if (ip)
905  {
907  }else
908  {
909  // Register dictionary and Contents observer
910  REGISTER_SHAREDPTR_OBSERVER(_dict, _wd);
911  // If it contains Contents register observer on it too
912  if (_dict->containsProperty(Specification::Page::CONTENTS))
913  {
914  shared_ptr<IProperty> prop = _dict->getProperty(Specification::Page::CONTENTS);
915  REGISTER_SHAREDPTR_OBSERVER(prop, _wd);
916  }
917  }
918 }
919 
920 //
921 //
922 //
923 void
924 CPageContents::unreg_observer (boost::shared_ptr<IProperty> ip) const
925 {
926  if (ip)
927  {
929  }else
930  {
931  // Unregister dictionary observer
932  UNREGISTER_SHAREDPTR_OBSERVER(_dict, _wd);
933  // Unregister contents observer
934  if (_dict->containsProperty(Specification::Page::CONTENTS))
935  {
936  shared_ptr<IProperty> prop = _dict->getProperty(Specification::Page::CONTENTS);
938  }
939  }
940 }
941 
942 
943 void
944 CPageContents::change (bool invalid)
945 {
946  _page->_objectChanged (invalid);
947 }
948 
949 void
950 CPageContents::_xpdf_display_params (boost::shared_ptr<GfxResources>& res,
951  boost::shared_ptr<GfxState>& state)
952 {
953  _page->display()->createXpdfDisplayParams (res, state);
954 }
955 
956 size_t
957 CPageContents::_page_pos () const
958 {
959  return _page->getPagePosition();
960 }
961 
962 //
963 //
964 //
965 void
966 CPageContents::moveAbove (shared_ptr<const CContentStream> ct)
967 {
968  // Get the next item
969  init();
970  CCs::iterator itNext = find (_ccs.begin(), _ccs.end(), ct);
971  if (itNext == _ccs.end())
972  throw CObjInvalidOperation ();
973  ++itNext;
974  if (itNext == _ccs.end())
975  throw OutOfRange ();
976 
977  // Delete next item but store it
978  shared_ptr<CContentStream> tmp = *itNext;
979  _ccs.erase (itNext, itNext + 1);
980  // Insert stored item before supplied (simply swap ct with the next item)
981  _ccs.insert (find (_ccs.begin(), _ccs.end(), ct), tmp);
982 
983  {
984  ContentsObserverFreeSection reg_lock (this);
985  setContents (_dict, _ccs);
986  } // Also change Contents entry of page dictionary
987 
988  // Indicate change
989  change ();
990 }
991 
992 //
993 //
994 //
995 void
996 CPageContents::moveBelow (shared_ptr<const CContentStream> ct)
997 {
998  // Get the item index
999  unsigned int pos = 0;
1000  init();
1001  for (pos = 0; pos < _ccs.size(); ++pos)
1002  if (_ccs[pos] == ct)
1003  break;
1004 
1005  // If first or not found throw exception
1006  if (pos == _ccs.size() || 0 == pos)
1007  throw CObjInvalidOperation ();
1008 
1009  // Swap
1010  shared_ptr<CContentStream> tmp = _ccs[pos];
1011  _ccs[pos] = _ccs[pos - 1];
1012  _ccs[pos - 1] = tmp;
1013 
1014  // Also change Contents entry of page dictionary
1015  {
1016  ContentsObserverFreeSection reg_lock (this);
1017  setContents (_dict, _ccs);
1018  } // Also change Contents entry of page dictionary
1019 
1020  // Indicate change
1021  change ();
1022 }
1023 
1024 
1025 //
1026 //
1027 //
1028 void
1030 {
1031  moveAbove (_page->contents()->getContentStream (pos));
1032 }
1033 
1034 //
1035 //
1036 //
1037 void
1039 {
1040  moveBelow (_page->contents()->getContentStream (pos));
1041 }
1042 
1043 void
1045 {
1046  // we already made a reset
1047  if (!_page)
1048  return;
1049  unreg_observer ();
1050  _page = NULL;
1051  _dict.reset ();
1052  _wd.reset ();
1053  assert (!_wd.use_count());
1054 }
1055 
1056 //==========================================================
1057 } // namespace pdfobjects
1058 //==========================================================