libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNAltoLayout.cpp
Go to the documentation of this file.
1 /* Copyright 2011-2016 CoReNum
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNAltoLayout.h
19  * \author Yann LEYDIER
20  */
21 
22 #include <CRNXml/CRNAlto.h>
23 #include <CRNException.h>
24 #include <algorithm>
25 #include <CRNi18n.h>
26 
27 using namespace crn;
28 using namespace xml;
29 
35 Alto::Layout::Layout(const Element &el):
36  Element(el)
37 {
38  if (!el)
39  throw ExceptionInvalidArgument(_("Null node."));
40  update_subelements();
41 }
42 
44 void Alto::Layout::update_subelements()
45 {
46  pages.clear();
47  id_pages.clear();
48 
49  Element pel = GetFirstChildElement("Page");
50  while (pel)
51  {
52  pages.push_back(std::shared_ptr<Page>(new Page(pel)));
53  id_pages[pages.back()->GetId()] = pages.back();
54  pel = pel.GetNextSiblingElement("Page");
55  }
56 }
57 
59 std::vector<Id> Alto::Layout::GetStyles() const
60 {
61  return GetStyleRefs(*this);
62 }
63 
67 void Alto::Layout::AddStyle(const Id &styleid)
68 {
69  AddStyleRef(*this, styleid);
70 }
71 
75 void Alto::Layout::RemoveStyle(const Id &styleid)
76 {
77  RemoveStyleRef(*this, styleid);
78 }
79 
85 Alto::Layout::Page& Alto::Layout::GetPage(const Id &pid)
86 {
87  if (GetNbSubelements() != pages.size())
88  const_cast<Layout*>(this)->update_subelements();
89  std::map<Id, std::weak_ptr<Page>>::iterator it(id_pages.find(pid));
90  if ((it != id_pages.end()) && !it->second.expired())
91  return *(it->second.lock());
92  for (const PagePtr &p : pages) // not that useful… still, can't stop thinking it might save our life…
93  {
94  const std::shared_ptr<Layout::Page> sp(p.lock());
95  if (sp->GetId() == pid)
96  {
97  id_pages[pid] = p;
98  return *sp;
99  }
100  }
101  throw crn::ExceptionNotFound(_("Page not found."));
102 }
103 
105 std::vector<Alto::Layout::PagePtr> Alto::Layout::GetPages() const
106 {
107  if (GetNbSubelements() != pages.size())
108  const_cast<Layout*>(this)->update_subelements();
109  return std::vector<PagePtr>(pages.begin(), pages.end());
110 }
111 
120 Alto::Layout::Page& Alto::Layout::AddPage(const Id &id_, int image_number, Option<int> width_, Option<int> height_, Option<Alto::Layout::Page::Position> pos)
121 {
122  pages.push_back(std::shared_ptr<Page>(new Page(PushBackElement("Page"), id_, image_number, width_, height_, pos)));
123  id_pages[id_] = pages.back();
124  return *pages.back();
125 }
126 
137 Alto::Layout::Page& Alto::Layout::AddPageAfter(const Id &pred, const Id &id_, int image_number, Option<int> width_, Option<int> height_, Option<Alto::Layout::Page::Position> pos)
138 {
139  for (std::vector<std::shared_ptr<Page> >::iterator it = pages.begin(); it != pages.end(); ++it)
140  {
141  if ((*it)->GetId() == pred)
142  {
143  Element pel(**it);
144  ++it;
145  if (it == pages.end())
146  return AddPage(id_, image_number, width_, height_, pos);
147  else
148  {
149  std::shared_ptr<Page> p(new Page(InsertElement(pel, "Page"), id_, image_number, width_, height_, pos));
150  pages.insert(it, p);
151  id_pages[id_] = p;
152  return *p;
153  }
154  }
155  }
156  throw crn::ExceptionNotFound(_("Page not found."));
157 }
158 
169 Alto::Layout::Page& Alto::Layout::AddPageBefore(const Id &next, const Id &id_, int image_number, Option<int> width_, Option<int> height_, Option<Alto::Layout::Page::Position> pos)
170 {
171  for (std::vector<std::shared_ptr<Page> >::iterator it = pages.begin(); it != pages.end(); ++it)
172  {
173  if ((*it)->GetId() == next)
174  {
175  std::shared_ptr<Page> newpage;
176  if (it == pages.begin())
177  newpage = std::shared_ptr<Page>(new Page(PushFrontElement("Page"), id_, image_number, width_, height_, pos));
178  else
179  newpage = std::shared_ptr<Page>(new Page(InsertElement(**(it - 1), "Page"), id_, image_number, width_, height_, pos));
180  pages.insert(it, newpage);
181  id_pages[id_] = newpage;
182  return *newpage;
183  }
184  }
185  throw crn::ExceptionNotFound(_("Page not found."));
186 }
187 
192 void Alto::Layout::RemovePage(const Id &pid)
193 {
194  for (std::vector<std::shared_ptr<Page> >::iterator it = pages.begin(); it != pages.end(); ++it)
195  {
196  if ((*it)->GetId() == pid)
197  {
198  RemoveChild(**it);
199  pages.erase(it);
200  id_pages.erase(pid);
201  }
202  }
203  throw crn::ExceptionNotFound(_("Page not found."));
204 }
205 
207 // Page
209 
214 Alto::Layout::Page::Page(const Element &el):
215  Element(el)
216 {
217  id = el.GetAttribute<StringUTF8>("ID", false); // may throw
218  el.GetAttribute<int>("PHYSICAL_IMG_NR", false); // may throw
219 
220  update_subelements();
221 }
222 
224 void Alto::Layout::Page::update_subelements()
225 {
226  spaces.clear();
227  id_spaces.clear();
228  topMargin = leftMargin = rightMargin = bottomMargin = printSpace = SpacePtr();
229 
230  for (Element cel = BeginElement(); cel != EndElement(); ++cel)
231  {
232  StringUTF8 elname(cel.GetName());
233  std::shared_ptr<Space> sp;
234  if (elname == "TopMargin")
235  {
236  sp = std::shared_ptr<Space>(new Space(cel));
237  topMargin = sp;
238  }
239  else if (elname == "LeftMargin")
240  {
241  sp = std::shared_ptr<Space>(new Space(cel));
242  leftMargin = sp;
243  }
244  else if (elname == "RightMargin")
245  {
246  sp = std::shared_ptr<Space>(new Space(cel));
247  rightMargin = sp;
248  }
249  else if (elname == "BottomMargin")
250  {
251  sp = std::shared_ptr<Space>(new Space(cel));
252  bottomMargin = sp;
253  }
254  else if (elname == "PrintSpace")
255  {
256  sp = std::shared_ptr<Space>(new Space(cel));
257  printSpace = sp;
258  }
259  if (sp)
260  {
261  spaces.push_back(sp);
262  if (sp->GetId())
263  id_spaces[sp->GetId().Get()] = sp;
264  }
265  }
266 }
267 
276 Alto::Layout::Page::Page(const Element &el, const Id &id_, int image_number, Option<int> width, Option<int> height, Option<Alto::Layout::Page::Position> position):
277  Element(el),
278  id(id_)
279 {
280  SetAttribute("ID", id);
281  SetAttribute("PHYSICAL_IMG_NR", image_number);
282  if (width)
283  SetAttribute("WIDTH", *width);
284  if (height)
285  SetAttribute("HEIGHT", *height);
286  if (position)
287  {
288  StringUTF8 av;
289  switch (*position)
290  {
291  case Position::Left:
292  av = "Left";
293  break;
294  case Position::Right:
295  av = "Right";
296  break;
297  case Position::Foldout:
298  av = "Foldout";
299  break;
300  case Position::Single:
301  av = "Single";
302  break;
303  case Position::Cover:
304  av = "Cover";
305  break;
306  default:
307  throw crn::ExceptionDomain(_("Invalid page position."));
308  }
309  SetAttribute("POSITION", av);
310  }
311 }
312 
314 std::vector<Id> Alto::Layout::Page::GetStyles() const
315 {
316  return GetStyleRefs(*this);
317 }
318 
322 void Alto::Layout::Page::AddStyle(const Id &styleid)
323 {
324  AddStyleRef(*this, styleid);
325 }
326 
330 void Alto::Layout::Page::RemoveStyle(const Id &styleid)
331 {
332  RemoveStyleRef(*this, styleid);
333 }
334 
336 Option<StringUTF8> Alto::Layout::Page::GetPageClass() const
337 {
338  Option<StringUTF8> pageClass;
339  StringUTF8 str = GetAttribute<StringUTF8>("PAGECLASS");
340  if (str.IsNotEmpty())
341  pageClass = str;
342  return pageClass;
343 }
344 
348 void Alto::Layout::Page::SetPageClass(const StringUTF8 &s)
349 {
350  SetAttribute("PAGECLASS", s);
351 }
352 
354 Option<int> Alto::Layout::Page::GetHeight() const
355 {
356  Option<int> height;
357  try { height = GetAttribute<int>("HEIGHT", false); } catch (...) {}
358  return height;
359 }
360 
362 void Alto::Layout::Page::SetHeight(int i)
363 {
364  SetAttribute("HEIGHT", i);
365 }
366 
368 Option<int> Alto::Layout::Page::GetWidth() const
369 {
370  Option<int> width;
371  try { width = GetAttribute<int>("WIDTH", false); } catch (...) {}
372  return width;
373 }
374 
376 void Alto::Layout::Page::SetWidth(int i)
377 {
378  SetAttribute("WIDTH", i);
379 }
380 
382 int Alto::Layout::Page::GetPhysicalImageNumber() const
383 {
384  return GetAttribute<int>("PHYSICAL_IMG_NR", false); // should not throw
385 }
386 
388 Option<StringUTF8> Alto::Layout::Page::GetPrintedImageNumber() const
389 {
390  Option<StringUTF8> printedImageNumber;
391  StringUTF8 str = GetAttribute<StringUTF8>("PRINTED_IMG_NR");
392  if (str.IsNotEmpty())
393  printedImageNumber = str;
394  return printedImageNumber;
395 }
396 
401 void Alto::Layout::Page::SetPhysicalImageNumber(int pnum)
402 {
403  if (pnum < 0)
404  throw crn::ExceptionDomain(_("Negative physical image number."));
405  SetAttribute("PHYSICAL_IMG_NR", pnum);
406 }
407 
411 void Alto::Layout::Page::SetPrintedImageNumber(const StringUTF8 &s)
412 {
413  SetAttribute("PRINTED_IMG_NR", s);
414 }
415 
417 Option<Alto::Layout::Page::Quality> Alto::Layout::Page::GetQuality() const
418 {
419  Option<Quality> quality;
420  StringUTF8 str = GetAttribute<StringUTF8>("QUALITY");
421  if (str.IsNotEmpty())
422  {
423  str.ToLower();
424  if (str == "OK")
425  quality = Quality::Ok;
426  else if (str == "Missing")
427  quality = Quality::Missing;
428  else if (str == "Missing in original")
429  quality = Quality::MissingInOriginal;
430  else if (str == "Damaged")
431  quality = Quality::Damaged;
432  else if (str == "Retained")
433  quality = Quality::Retained;
434  else if (str == "Target")
435  quality = Quality::Target;
436  else if (str == "As in original")
437  quality = Quality::AsInOriginal;
438  }
439  return quality;
440 }
441 
446 void Alto::Layout::Page::SetQuality(Quality q)
447 {
448  StringUTF8 attr;
449  switch (q)
450  {
451  case Quality::Ok:
452  attr = "OK";
453  break;
454  case Quality::Missing:
455  attr = "Missing";
456  break;
457  case Quality::MissingInOriginal:
458  attr = "Missing in original";
459  break;
460  case Quality::Damaged:
461  attr = "Damaged";
462  break;
463  case Quality::Retained:
464  attr = "Retained";
465  break;
466  case Quality::Target:
467  attr = "Target";
468  break;
469  case Quality::AsInOriginal:
470  attr = "As in original";
471  break;
472  default:
473  throw crn::ExceptionDomain(_("Invalid quality."));
474  }
475  SetAttribute("QUALITY", attr);
476 }
477 
479 Option<StringUTF8> Alto::Layout::Page::GetQualityDetail() const
480 {
481  Option<StringUTF8> qualityDetail;
482  StringUTF8 str = GetAttribute<StringUTF8>("QUALITY_DETAIL");
483  if (str.IsNotEmpty())
484  qualityDetail = str;
485  return qualityDetail;
486 }
487 
491 void Alto::Layout::Page::SetQualityDetail(const StringUTF8 &s)
492 {
493  SetAttribute("QUALITY_DETAIL", s);
494 }
495 
497 Option<Alto::Layout::Page::Position> Alto::Layout::Page::GetPosition() const
498 {
499  Option<Position> position;
500  StringUTF8 str = GetAttribute<StringUTF8>("POSITION");
501  if (str.IsNotEmpty())
502  {
503  str.ToLower();
504  if (str == "Left")
505  position = Position::Left;
506  else if (str == "Right")
507  position = Position::Right;
508  else if (str == "Foldout")
509  position = Position::Foldout;
510  else if (str == "Single")
511  position = Position::Single;
512  else if (str == "Cover")
513  position = Position::Cover;
514  }
515  return position;
516 }
517 
519 Option<Id> Alto::Layout::Page::GetProcessing() const
520 {
521  Option<Id> processing;
522  StringUTF8 str = GetAttribute<StringUTF8>("PROCESSING");
523  if (str.IsNotEmpty())
524  processing = str;
525  return processing;
526 }
527 
529 Option<double> Alto::Layout::Page::GetAccuracy() const
530 {
531  Option<double> accuracy;
532  try { accuracy = GetAttribute<double>("ACCURACY", false); } catch (...) {}
533  return accuracy;
534 }
535 
540 void Alto::Layout::Page::SetAccuracy(double acc)
541 {
542  if ((acc < 0.0) || (acc > 100.0))
543  throw crn::ExceptionDomain(_("The page accuracy must be in [0, 100]"));
544  SetAttribute("ACCURACY", acc);
545 }
546 
548 Option<double> Alto::Layout::Page::GetPageConfidence() const
549 {
550  Option<double> pageConfidence;
551  try { pageConfidence = GetAttribute<double>("PC", false); } catch (...) {}
552  return pageConfidence;
553 }
554 
559 void Alto::Layout::Page::SetPageConfidence(double c)
560 {
561  if ((c < 0.0) || (c > 100.0))
562  throw crn::ExceptionDomain(_("The page confidence must be in [0, 1]"));
563  SetAttribute("PC", c);
564 }
565 
574 Alto::Layout::Page::Space& Alto::Layout::Page::AddTopMargin(const Id &id_, double x, double y, double w, double h)
575 {
576  if (!topMargin.expired())
577  throw crn::ExceptionLogic(_("The page already has a top margin."));
578  spaces.push_back(std::shared_ptr<Space>(new Space(PushBackElement("TopMargin"), id_, x, y, w, h)));
579  id_spaces[id_] = spaces.back();
580  topMargin = spaces.back();
581  return *topMargin.lock();
582 }
583 
592 Alto::Layout::Page::Space& Alto::Layout::Page::AddLeftMargin(const Id &id_, double x, double y, double w, double h)
593 {
594  if (!leftMargin.expired())
595  throw crn::ExceptionLogic(_("The page already has a left margin."));
596  spaces.push_back(std::shared_ptr<Space>(new Space(PushBackElement("LeftMargin"), id_, x, y, w, h)));
597  id_spaces[id_] = spaces.back();
598  leftMargin = spaces.back();
599  return *leftMargin.lock();
600 }
601 
610 Alto::Layout::Page::Space& Alto::Layout::Page::AddRightMargin(const Id &id_, double x, double y, double w, double h)
611 {
612  if (!rightMargin.expired())
613  throw crn::ExceptionLogic(_("The page already has a right margin."));
614  spaces.push_back(std::shared_ptr<Space>(new Space(PushBackElement("RightMargin"), id_, x, y, w, h)));
615  id_spaces[id_] = spaces.back();
616  rightMargin = spaces.back();
617  return *rightMargin.lock();
618 }
619 
628 Alto::Layout::Page::Space& Alto::Layout::Page::AddBottomMargin(const Id &id_, double x, double y, double w, double h)
629 {
630  if (!bottomMargin.expired())
631  throw crn::ExceptionLogic(_("The page already has a bottom margin."));
632  spaces.push_back(std::shared_ptr<Space>(new Space(PushBackElement("BottomMargin"), id_, x, y, w, h)));
633  id_spaces[id_] = spaces.back();
634  bottomMargin = spaces.back();
635  return *bottomMargin.lock();
636 }
637 
646 Alto::Layout::Page::Space& Alto::Layout::Page::AddPrintSpace(const Id &id_, double x, double y, double w, double h)
647 {
648  if (!printSpace.expired())
649  throw crn::ExceptionLogic(_("The page already has a print space."));
650  spaces.push_back(std::shared_ptr<Space>(new Space(PushBackElement("PrintSpace"), id_, x, y, w, h)));
651  id_spaces[id_] = spaces.back();
652  printSpace = spaces.back();
653  return *printSpace.lock();
654 }
655 
661 Alto::Layout::Page::Space& Alto::Layout::Page::GetSpace(const Id &sid)
662 {
663  if (GetNbSubelements() != spaces.size())
664  const_cast<Page*>(this)->update_subelements();
665  std::map<Id, std::weak_ptr<Space> >::iterator it(id_spaces.find(sid));
666  if ((it != id_spaces.end()) && !it->second.expired())
667  return *(it->second.lock());
668  for (const SpacePtr &s : spaces)
669  {
670  const std::shared_ptr<Layout::Page::Space> ss(s.lock());
671  if (ss->GetId().Get() == sid)
672  {
673  id_spaces[sid] = s;
674  return *ss;
675  }
676  }
677  throw crn::ExceptionNotFound(_("Space not found."));
678 }
679 
681 std::vector<Alto::Layout::Page::SpacePtr> Alto::Layout::Page::GetSpaces() const
682 {
683  if (GetNbSubelements() != spaces.size())
684  const_cast<Page*>(this)->update_subelements();
685 
686  return std::vector<SpacePtr>(spaces.begin(), spaces.end());
687 }
688 
693 void Alto::Layout::Page::RemoveSpace(const Id &sid)
694 {
695  for (std::vector<std::shared_ptr<Space> >::iterator it = spaces.begin(); it != spaces.end(); ++it)
696  if ((*it)->GetId().Get() == sid)
697  {
698  RemoveChild(**it);
699  spaces.erase(it);
700  id_spaces.erase(sid);
701  return;
702  }
703  throw crn::ExceptionNotFound(_("Space not found."));
704 }
705 
StringUTF8 & ToLower()
Converts the string to lowercase.
A print space on a page.
XML element.
Definition: CRNXml.h:135
#define _(String)
Definition: CRNi18n.h:51
std::vector< Id > GetStyleRefs(const Element &el)
Gets the list of style references.
bool IsNotEmpty() const noexcept
Checks if the string is not empty.
Element GetNextSiblingElement(const StringUTF8 &name="")
Gets the next sibling element.
Definition: CRNXml.cpp:247
A generic logic error.
Definition: CRNException.h:71
void AddStyleRef(Element &el, const Id &id)
Adds a style reference to an element.
A generic domain error.
Definition: CRNException.h:83
void RemoveStyleRef(Element &el, const Id &id)
Removes a style reference to an element.
T GetAttribute(const StringUTF8 &name, bool silent=true) const
Gets an attribute.
Definition: CRNXml.h:219
A character string class.
Definition: CRNStringUTF8.h:49
A class to store an optional value.
Definition: CRNOption.h:33
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107
An item was not found in a container.
Definition: CRNException.h:95