xref: /webtrees/app/Report/ReportParserGenerate.php (revision 8edd104350eecf7d79de0350f051fb55d7633f79)
1a6f13a4aSGreg Roach<?php
2a6f13a4aSGreg Roachnamespace Fisharebest\Webtrees\Report;
3a6f13a4aSGreg Roach
4a6f13a4aSGreg Roach/**
5a6f13a4aSGreg Roach * webtrees: online genealogy
6a6f13a4aSGreg Roach * Copyright (C) 2015 webtrees development team
7a6f13a4aSGreg Roach * This program is free software: you can redistribute it and/or modify
8a6f13a4aSGreg Roach * it under the terms of the GNU General Public License as published by
9a6f13a4aSGreg Roach * the Free Software Foundation, either version 3 of the License, or
10a6f13a4aSGreg Roach * (at your option) any later version.
11a6f13a4aSGreg Roach * This program is distributed in the hope that it will be useful,
12a6f13a4aSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
13a6f13a4aSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14a6f13a4aSGreg Roach * GNU General Public License for more details.
15a6f13a4aSGreg Roach * You should have received a copy of the GNU General Public License
16a6f13a4aSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
17a6f13a4aSGreg Roach */
18a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Auth;
19a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Database;
20a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Date;
21a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Family;
22a6f13a4aSGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
23a6f13a4aSGreg Roachuse Fisharebest\Webtrees\GedcomTag;
24a6f13a4aSGreg Roachuse Fisharebest\Webtrees\I18N;
25a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Individual;
26a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Media;
27a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Place;
28a6f13a4aSGreg Roach
29a6f13a4aSGreg Roach/**
30a6f13a4aSGreg Roach * Class ReportParserGenerate - parse a report.xml file and generate the report.
31a6f13a4aSGreg Roach */
32a6f13a4aSGreg Roachclass ReportParserGenerate extends ReportParserBase {
33a6f13a4aSGreg Roach	/** @var bool Are we processing a <Description> element  */
34a6f13a4aSGreg Roach	private $report_description = false;
35a6f13a4aSGreg Roach
36a6f13a4aSGreg Roach	/** @var bool Are we processing a <Title> element  */
37a6f13a4aSGreg Roach	private $report_title = false;
38a6f13a4aSGreg Roach
39a6f13a4aSGreg Roach	/** @var bool Are we collecting data from <Footnote> elements  */
40a6f13a4aSGreg Roach	private $process_footnote = true;
41a6f13a4aSGreg Roach
42a6f13a4aSGreg Roach	/** @var bool Are we currently outputing data? */
43a6f13a4aSGreg Roach	private $print_data = false;
44a6f13a4aSGreg Roach
45a6f13a4aSGreg Roach	/** @var bool[] Push-down stack of $print_data */
46a6f13a4aSGreg Roach	private $print_data_stack = array();
47a6f13a4aSGreg Roach
48a6f13a4aSGreg Roach	/** @var int */
49a6f13a4aSGreg Roach	private $process_gedcoms = 0;
50a6f13a4aSGreg Roach
51a6f13a4aSGreg Roach	/** @var int */
52a6f13a4aSGreg Roach	private $process_ifs = 0;
53a6f13a4aSGreg Roach
54a6f13a4aSGreg Roach	/** @var int */
55a6f13a4aSGreg Roach	private $process_repeats = 0;
56a6f13a4aSGreg Roach
57a6f13a4aSGreg Roach	/** @var int Quantity of data to repeat during loops */
58a6f13a4aSGreg Roach	private $repeat_bytes = 0;
59a6f13a4aSGreg Roach
60a6f13a4aSGreg Roach	/** @var array[] Repeated data when iterating over loops */
61a6f13a4aSGreg Roach	private $repeats = array();
62a6f13a4aSGreg Roach
63a6f13a4aSGreg Roach	/** @var array[] Nested repeating data */
64a6f13a4aSGreg Roach	private $repeats_stack = array();
65a6f13a4aSGreg Roach
66a6f13a4aSGreg Roach	/** @var string The current GEDCOM record */
67a6f13a4aSGreg Roach	private $gedrec = '';
68a6f13a4aSGreg Roach
69a6f13a4aSGreg Roach	/** @var string[] Nested GEDCOM records */
70a6f13a4aSGreg Roach	private $gedrec_stack = array();
71a6f13a4aSGreg Roach
72a6f13a4aSGreg Roach	/** @var ReportBaseElement The currently processed element */
73a6f13a4aSGreg Roach	private $current_element;
74a6f13a4aSGreg Roach
75a6f13a4aSGreg Roach	/** @var ReportBaseElement The currently processed element */
76a6f13a4aSGreg Roach	private $footnote_element;
77a6f13a4aSGreg Roach
78a6f13a4aSGreg Roach	/** @var string The GEDCOM fact currently being processed */
79a6f13a4aSGreg Roach	private $fact = '';
80a6f13a4aSGreg Roach
81a6f13a4aSGreg Roach	/** @var string The GEDCOM value currently being processed */
82a6f13a4aSGreg Roach	private $desc = '';
83a6f13a4aSGreg Roach
84a6f13a4aSGreg Roach	/** @var string The GEDCOM type currently being processed */
85a6f13a4aSGreg Roach	private $type = '';
86a6f13a4aSGreg Roach
87a6f13a4aSGreg Roach	/** @var int The current generational level */
88a6f13a4aSGreg Roach	private $generation = 1;
89a6f13a4aSGreg Roach
90a6f13a4aSGreg Roach	/** @var array Source data for processing lists */
91a6f13a4aSGreg Roach	private $list = array();
92a6f13a4aSGreg Roach
93a6f13a4aSGreg Roach	/** @var int Number of items in lists */
94a6f13a4aSGreg Roach	private $list_total = 0;
95a6f13a4aSGreg Roach
96a6f13a4aSGreg Roach	/** @var int Number of items filtered from lists */
97a6f13a4aSGreg Roach	private $list_private = 0;
98a6f13a4aSGreg Roach
99a6f13a4aSGreg Roach	/** {@inheritDoc} */
100a6f13a4aSGreg Roach	public function __construct($report) {
101a6f13a4aSGreg Roach		$this->current_element = new ReportBaseElement;
102a6f13a4aSGreg Roach		parent::__construct($report);
103a6f13a4aSGreg Roach	}
104a6f13a4aSGreg Roach
105a6f13a4aSGreg Roach	/**
106a6f13a4aSGreg Roach	 *XML start element handler
107a6f13a4aSGreg Roach	 *
108a6f13a4aSGreg Roach	 * This function is called whenever a starting element is reached
109a6f13a4aSGreg Roach	 * The element handler will be called if found, otherwise it must be HTML
110a6f13a4aSGreg Roach	 *
111a6f13a4aSGreg Roach	 * @param resource $parser the resource handler for the XML parser
112a6f13a4aSGreg Roach	 * @param string   $name   the name of the XML element parsed
113a6f13a4aSGreg Roach	 * @param array    $attrs  an array of key value pairs for the attributes
114a6f13a4aSGreg Roach	 */
115*8edd1043SGreg Roach	protected function startElement($parser, $name, $attrs) {
116a6f13a4aSGreg Roach		global $vars;
117a6f13a4aSGreg Roach
118a6f13a4aSGreg Roach		$newattrs = array();
119a6f13a4aSGreg Roach
120a6f13a4aSGreg Roach		foreach ($attrs as $key => $value) {
121a6f13a4aSGreg Roach			if (preg_match("/^\\$(\w+)$/", $value, $match)) {
122a6f13a4aSGreg Roach				if ((isset($vars[$match[1]]['id'])) && (!isset($vars[$match[1]]['gedcom']))) {
123a6f13a4aSGreg Roach					$value = $vars[$match[1]]['id'];
124a6f13a4aSGreg Roach				}
125a6f13a4aSGreg Roach			}
126a6f13a4aSGreg Roach			$newattrs[$key] = $value;
127a6f13a4aSGreg Roach		}
128a6f13a4aSGreg Roach		$attrs = $newattrs;
129a6f13a4aSGreg Roach		if ($this->process_footnote && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag")) {
130a6f13a4aSGreg Roach			$start_method = $name . 'StartHandler';
131a6f13a4aSGreg Roach			$end_method   = $name . 'EndHandler';
132a6f13a4aSGreg Roach			if (method_exists($this, $start_method)) {
133a6f13a4aSGreg Roach				$this->$start_method($attrs);
134a6f13a4aSGreg Roach			} elseif (!method_exists($this, $end_method)) {
135a6f13a4aSGreg Roach				$this->htmlStartHandler($name, $attrs);
136a6f13a4aSGreg Roach			}
137a6f13a4aSGreg Roach		}
138a6f13a4aSGreg Roach	}
139a6f13a4aSGreg Roach
140a6f13a4aSGreg Roach	/**
141a6f13a4aSGreg Roach	 * XML end element handler
142a6f13a4aSGreg Roach	 *
143a6f13a4aSGreg Roach	 * This function is called whenever an ending element is reached
144a6f13a4aSGreg Roach	 * The element handler will be called if found, otherwise it must be HTML
145a6f13a4aSGreg Roach	 *
146a6f13a4aSGreg Roach	 * @param resource $parser the resource handler for the XML parser
147a6f13a4aSGreg Roach	 * @param string   $name   the name of the XML element parsed
148a6f13a4aSGreg Roach	 */
149*8edd1043SGreg Roach	protected function endElement($parser, $name) {
150a6f13a4aSGreg Roach		if (($this->process_footnote || $name === "Footnote") && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag" || $name === "List" || $name === "Relatives")) {
151a6f13a4aSGreg Roach			$start_method = $name . 'StartHandler';
152a6f13a4aSGreg Roach			$end_method   = $name . 'EndHandler';
153a6f13a4aSGreg Roach			if (method_exists($this, $end_method)) {
154a6f13a4aSGreg Roach				$this->$end_method();
155a6f13a4aSGreg Roach			} elseif (!method_exists($this, $start_method)) {
156a6f13a4aSGreg Roach				$this->htmlEndHandler($name);
157a6f13a4aSGreg Roach			}
158a6f13a4aSGreg Roach		}
159a6f13a4aSGreg Roach	}
160a6f13a4aSGreg Roach
161a6f13a4aSGreg Roach	/**
162a6f13a4aSGreg Roach	 * XML character data handler
163a6f13a4aSGreg Roach	 *
164a6f13a4aSGreg Roach	 * @param resource $parser the resource handler for the XML parser
165a6f13a4aSGreg Roach	 * @param string   $data   the name of the XML element parsed
166a6f13a4aSGreg Roach	 */
167*8edd1043SGreg Roach	protected function characterData($parser, $data) {
168a6f13a4aSGreg Roach		global $wt_report;
169a6f13a4aSGreg Roach
170a6f13a4aSGreg Roach		if ($this->print_data && ($this->process_gedcoms === 0) && ($this->process_ifs === 0) && ($this->process_repeats === 0)) {
171a6f13a4aSGreg Roach			$this->current_element->addText($data);
172a6f13a4aSGreg Roach		} elseif ($this->report_title) {
173a6f13a4aSGreg Roach			$wt_report->addTitle($data);
174a6f13a4aSGreg Roach		} elseif ($this->report_description) {
175a6f13a4aSGreg Roach			$wt_report->addDescription($data);
176a6f13a4aSGreg Roach		}
177a6f13a4aSGreg Roach	}
178a6f13a4aSGreg Roach
179a6f13a4aSGreg Roach	/**
180a6f13a4aSGreg Roach	 * XML <style> start element handler
181a6f13a4aSGreg Roach	 *
182a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
183a6f13a4aSGreg Roach	 */
184*8edd1043SGreg Roach	private function styleStartHandler($attrs) {
185a6f13a4aSGreg Roach		global $wt_report;
186a6f13a4aSGreg Roach
187a6f13a4aSGreg Roach		if (empty($attrs['name'])) {
188a6f13a4aSGreg Roach			throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
189a6f13a4aSGreg Roach		}
190a6f13a4aSGreg Roach
191a6f13a4aSGreg Roach		// array Style that will be passed on
192a6f13a4aSGreg Roach		$s = array();
193a6f13a4aSGreg Roach
194a6f13a4aSGreg Roach		// string Name af the style
195a6f13a4aSGreg Roach		$s['name'] = $attrs['name'];
196a6f13a4aSGreg Roach
197a6f13a4aSGreg Roach		// string Name of the DEFAULT font
198a6f13a4aSGreg Roach		$s['font'] = $wt_report->defaultFont;
199a6f13a4aSGreg Roach		if (!empty($attrs['font'])) {
200a6f13a4aSGreg Roach			$s['font'] = $attrs['font'];
201a6f13a4aSGreg Roach		}
202a6f13a4aSGreg Roach
203a6f13a4aSGreg Roach		// int The size of the font in points
204a6f13a4aSGreg Roach		$s['size'] = $wt_report->defaultFontSize;
205a6f13a4aSGreg Roach		if (!empty($attrs['size'])) {
206a6f13a4aSGreg Roach			$s['size'] = (int) $attrs['size'];
207a6f13a4aSGreg Roach		} // Get it as int to ignore all decimal points or text (if any text then int(0))
208a6f13a4aSGreg Roach
209a6f13a4aSGreg Roach		// string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
210a6f13a4aSGreg Roach		$s['style'] = "";
211a6f13a4aSGreg Roach		if (!empty($attrs['style'])) {
212a6f13a4aSGreg Roach			$s['style'] = $attrs['style'];
213a6f13a4aSGreg Roach		}
214a6f13a4aSGreg Roach
215a6f13a4aSGreg Roach		$wt_report->addStyle($s);
216a6f13a4aSGreg Roach	}
217a6f13a4aSGreg Roach
218a6f13a4aSGreg Roach	/**
219a6f13a4aSGreg Roach	 * XML <Doc> start element handler
220a6f13a4aSGreg Roach	 *
221a6f13a4aSGreg Roach	 * Sets up the basics of the document proparties
222a6f13a4aSGreg Roach	 *
223a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
224a6f13a4aSGreg Roach	 */
225*8edd1043SGreg Roach	private function docStartHandler($attrs) {
226a6f13a4aSGreg Roach		global $parser, $wt_report;
227a6f13a4aSGreg Roach
228a6f13a4aSGreg Roach		$parser = $this->xml_parser;
229a6f13a4aSGreg Roach
230a6f13a4aSGreg Roach		// Custom page width
231a6f13a4aSGreg Roach		if (!empty($attrs['customwidth'])) {
232a6f13a4aSGreg Roach			$wt_report->pagew = (int) $attrs['customwidth'];
233a6f13a4aSGreg Roach		} // Get it as int to ignore all decimal points or text (if any text then int(0))
234a6f13a4aSGreg Roach		// Custom Page height
235a6f13a4aSGreg Roach		if (!empty($attrs['customheight'])) {
236a6f13a4aSGreg Roach			$wt_report->pageh = (int) $attrs['customheight'];
237a6f13a4aSGreg Roach		} // Get it as int to ignore all decimal points or text (if any text then int(0))
238a6f13a4aSGreg Roach
239a6f13a4aSGreg Roach		// Left Margin
240a6f13a4aSGreg Roach		if (isset($attrs['leftmargin'])) {
241a6f13a4aSGreg Roach			if ($attrs['leftmargin'] === "0") {
242a6f13a4aSGreg Roach				$wt_report->leftmargin = 0;
243a6f13a4aSGreg Roach			} elseif (!empty($attrs['leftmargin'])) {
244a6f13a4aSGreg Roach				$wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
245a6f13a4aSGreg Roach			}
246a6f13a4aSGreg Roach		}
247a6f13a4aSGreg Roach		// Right Margin
248a6f13a4aSGreg Roach		if (isset($attrs['rightmargin'])) {
249a6f13a4aSGreg Roach			if ($attrs['rightmargin'] === "0") {
250a6f13a4aSGreg Roach				$wt_report->rightmargin = 0;
251a6f13a4aSGreg Roach			} elseif (!empty($attrs['rightmargin'])) {
252a6f13a4aSGreg Roach				$wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
253a6f13a4aSGreg Roach			}
254a6f13a4aSGreg Roach		}
255a6f13a4aSGreg Roach		// Top Margin
256a6f13a4aSGreg Roach		if (isset($attrs['topmargin'])) {
257a6f13a4aSGreg Roach			if ($attrs['topmargin'] === "0") {
258a6f13a4aSGreg Roach				$wt_report->topmargin = 0;
259a6f13a4aSGreg Roach			} elseif (!empty($attrs['topmargin'])) {
260a6f13a4aSGreg Roach				$wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
261a6f13a4aSGreg Roach			}
262a6f13a4aSGreg Roach		}
263a6f13a4aSGreg Roach		// Bottom Margin
264a6f13a4aSGreg Roach		if (isset($attrs['bottommargin'])) {
265a6f13a4aSGreg Roach			if ($attrs['bottommargin'] === "0") {
266a6f13a4aSGreg Roach				$wt_report->bottommargin = 0;
267a6f13a4aSGreg Roach			} elseif (!empty($attrs['bottommargin'])) {
268a6f13a4aSGreg Roach				$wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
269a6f13a4aSGreg Roach			}
270a6f13a4aSGreg Roach		}
271a6f13a4aSGreg Roach		// Header Margin
272a6f13a4aSGreg Roach		if (isset($attrs['headermargin'])) {
273a6f13a4aSGreg Roach			if ($attrs['headermargin'] === "0") {
274a6f13a4aSGreg Roach				$wt_report->headermargin = 0;
275a6f13a4aSGreg Roach			} elseif (!empty($attrs['headermargin'])) {
276a6f13a4aSGreg Roach				$wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
277a6f13a4aSGreg Roach			}
278a6f13a4aSGreg Roach		}
279a6f13a4aSGreg Roach		// Footer Margin
280a6f13a4aSGreg Roach		if (isset($attrs['footermargin'])) {
281a6f13a4aSGreg Roach			if ($attrs['footermargin'] === "0") {
282a6f13a4aSGreg Roach				$wt_report->footermargin = 0;
283a6f13a4aSGreg Roach			} elseif (!empty($attrs['footermargin'])) {
284a6f13a4aSGreg Roach				$wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
285a6f13a4aSGreg Roach			}
286a6f13a4aSGreg Roach		}
287a6f13a4aSGreg Roach
288a6f13a4aSGreg Roach		// Page Orientation
289a6f13a4aSGreg Roach		if (!empty($attrs['orientation'])) {
290a6f13a4aSGreg Roach			if ($attrs['orientation'] == "landscape") {
291a6f13a4aSGreg Roach				$wt_report->orientation = "landscape";
292a6f13a4aSGreg Roach			} elseif ($attrs['orientation'] == "portrait") {
293a6f13a4aSGreg Roach				$wt_report->orientation = "portrait";
294a6f13a4aSGreg Roach			}
295a6f13a4aSGreg Roach		}
296a6f13a4aSGreg Roach		// Page Size
297a6f13a4aSGreg Roach		if (!empty($attrs['pageSize'])) {
298a6f13a4aSGreg Roach			$wt_report->pageFormat = strtoupper($attrs['pageSize']);
299a6f13a4aSGreg Roach		}
300a6f13a4aSGreg Roach
301a6f13a4aSGreg Roach		// Show Generated By...
302a6f13a4aSGreg Roach		if (isset($attrs['showGeneratedBy'])) {
303a6f13a4aSGreg Roach			if ($attrs['showGeneratedBy'] === "0") {
304a6f13a4aSGreg Roach				$wt_report->showGenText = false;
305a6f13a4aSGreg Roach			} elseif ($attrs['showGeneratedBy'] === "1") {
306a6f13a4aSGreg Roach				$wt_report->showGenText = true;
307a6f13a4aSGreg Roach			}
308a6f13a4aSGreg Roach		}
309a6f13a4aSGreg Roach
310a6f13a4aSGreg Roach		$wt_report->setup();
311a6f13a4aSGreg Roach	}
312a6f13a4aSGreg Roach
313a6f13a4aSGreg Roach	/**
314a6f13a4aSGreg Roach	 * XML </Doc> end element handler
315a6f13a4aSGreg Roach	 */
316*8edd1043SGreg Roach	private function docEndHandler() {
317a6f13a4aSGreg Roach		global $wt_report;
318a6f13a4aSGreg Roach		$wt_report->run();
319a6f13a4aSGreg Roach	}
320a6f13a4aSGreg Roach
321a6f13a4aSGreg Roach	/**
322a6f13a4aSGreg Roach	 * XML <Header> start element handler
323a6f13a4aSGreg Roach	 */
324*8edd1043SGreg Roach	private function headerStartHandler() {
325a6f13a4aSGreg Roach		global $wt_report;
326a6f13a4aSGreg Roach
327a6f13a4aSGreg Roach		// Clear the Header before any new elements are added
328a6f13a4aSGreg Roach		$wt_report->clearHeader();
329a6f13a4aSGreg Roach		$wt_report->setProcessing("H");
330a6f13a4aSGreg Roach	}
331a6f13a4aSGreg Roach
332a6f13a4aSGreg Roach	/**
333a6f13a4aSGreg Roach	 * XML <PageHeader> start element handler
334a6f13a4aSGreg Roach	 */
335*8edd1043SGreg Roach	private function pageHeaderStartHandler() {
336a6f13a4aSGreg Roach		global $wt_reportStack, $wt_report, $ReportRoot;
337a6f13a4aSGreg Roach
338a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
339a6f13a4aSGreg Roach		$this->print_data = false;
340a6f13a4aSGreg Roach		array_push($wt_reportStack, $wt_report);
341a6f13a4aSGreg Roach		$wt_report = $ReportRoot->createPageHeader();
342a6f13a4aSGreg Roach	}
343a6f13a4aSGreg Roach
344a6f13a4aSGreg Roach	/**
345a6f13a4aSGreg Roach	 * XML <pageHeaderEndHandler> end element handler
346a6f13a4aSGreg Roach	 */
347*8edd1043SGreg Roach	private function pageHeaderEndHandler() {
348a6f13a4aSGreg Roach		global $wt_report, $wt_reportStack;
349a6f13a4aSGreg Roach
350a6f13a4aSGreg Roach		$this->print_data        = array_pop($this->print_data_stack);
351a6f13a4aSGreg Roach		$this->current_element   = $wt_report;
352a6f13a4aSGreg Roach		$wt_report               = array_pop($wt_reportStack);
353a6f13a4aSGreg Roach		$wt_report->addElement($this->current_element);
354a6f13a4aSGreg Roach	}
355a6f13a4aSGreg Roach
356a6f13a4aSGreg Roach	/**
357a6f13a4aSGreg Roach	 * XML <bodyStartHandler> start element handler
358a6f13a4aSGreg Roach	 */
359*8edd1043SGreg Roach	private function bodyStartHandler() {
360a6f13a4aSGreg Roach		global $wt_report;
361a6f13a4aSGreg Roach		$wt_report->setProcessing("B");
362a6f13a4aSGreg Roach	}
363a6f13a4aSGreg Roach
364a6f13a4aSGreg Roach	/**
365a6f13a4aSGreg Roach	 * XML <footerStartHandler> start element handler
366a6f13a4aSGreg Roach	 */
367*8edd1043SGreg Roach	private function footerStartHandler() {
368a6f13a4aSGreg Roach		global $wt_report;
369a6f13a4aSGreg Roach		$wt_report->setProcessing("F");
370a6f13a4aSGreg Roach	}
371a6f13a4aSGreg Roach
372a6f13a4aSGreg Roach	/**
373a6f13a4aSGreg Roach	 * XML <Cell> start element handler
374a6f13a4aSGreg Roach	 *
375a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
376a6f13a4aSGreg Roach	 */
377*8edd1043SGreg Roach	private function cellStartHandler($attrs) {
378a6f13a4aSGreg Roach		global $ReportRoot, $wt_report;
379a6f13a4aSGreg Roach
380a6f13a4aSGreg Roach		// string The text alignment of the text in this box.
381a6f13a4aSGreg Roach		$align = "";
382a6f13a4aSGreg Roach		if (!empty($attrs['align'])) {
383a6f13a4aSGreg Roach			$align = $attrs['align'];
384a6f13a4aSGreg Roach			// RTL supported left/right alignment
385a6f13a4aSGreg Roach			if ($align == "rightrtl") {
386a6f13a4aSGreg Roach				if ($wt_report->rtl) {
387a6f13a4aSGreg Roach					$align = "left";
388a6f13a4aSGreg Roach				} else {
389a6f13a4aSGreg Roach					$align = "right";
390a6f13a4aSGreg Roach				}
391a6f13a4aSGreg Roach			} elseif ($align == "leftrtl") {
392a6f13a4aSGreg Roach				if ($wt_report->rtl) {
393a6f13a4aSGreg Roach					$align = "right";
394a6f13a4aSGreg Roach				} else {
395a6f13a4aSGreg Roach					$align = "left";
396a6f13a4aSGreg Roach				}
397a6f13a4aSGreg Roach			}
398a6f13a4aSGreg Roach		}
399a6f13a4aSGreg Roach
400a6f13a4aSGreg Roach		// string The color to fill the background of this cell
401a6f13a4aSGreg Roach		$bgcolor = "";
402a6f13a4aSGreg Roach		if (!empty($attrs['bgcolor'])) {
403a6f13a4aSGreg Roach			$bgcolor = $attrs['bgcolor'];
404a6f13a4aSGreg Roach		}
405a6f13a4aSGreg Roach
406a6f13a4aSGreg Roach		// int Whether or not the background should be painted
407a6f13a4aSGreg Roach		$fill = 1;
408a6f13a4aSGreg Roach		if (isset($attrs['fill'])) {
409a6f13a4aSGreg Roach			if ($attrs['fill'] === "0") {
410a6f13a4aSGreg Roach				$fill = 0;
411a6f13a4aSGreg Roach			} elseif ($attrs['fill'] === "1") {
412a6f13a4aSGreg Roach				$fill = 1;
413a6f13a4aSGreg Roach			}
414a6f13a4aSGreg Roach		}
415a6f13a4aSGreg Roach
416a6f13a4aSGreg Roach		$reseth = true;
417a6f13a4aSGreg Roach		// boolean   if true reset the last cell height (default true)
418a6f13a4aSGreg Roach		if (isset($attrs['reseth'])) {
419a6f13a4aSGreg Roach			if ($attrs['reseth'] === "0") {
420a6f13a4aSGreg Roach				$reseth = false;
421a6f13a4aSGreg Roach			} elseif ($attrs['reseth'] === "1") {
422a6f13a4aSGreg Roach				$reseth = true;
423a6f13a4aSGreg Roach			}
424a6f13a4aSGreg Roach		}
425a6f13a4aSGreg Roach
426a6f13a4aSGreg Roach		// mixed Whether or not a border should be printed around this box
427a6f13a4aSGreg Roach		$border = 0;
428a6f13a4aSGreg Roach		if (!empty($attrs['border'])) {
429a6f13a4aSGreg Roach			$border = $attrs['border'];
430a6f13a4aSGreg Roach		}
431a6f13a4aSGreg Roach		// string Border color in HTML code
432a6f13a4aSGreg Roach		$bocolor = "";
433a6f13a4aSGreg Roach		if (!empty($attrs['bocolor'])) {
434a6f13a4aSGreg Roach			$bocolor = $attrs['bocolor'];
435a6f13a4aSGreg Roach		}
436a6f13a4aSGreg Roach
437a6f13a4aSGreg Roach		// int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
438a6f13a4aSGreg Roach		$height = 0;
439a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
440a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
441a6f13a4aSGreg Roach		}
442a6f13a4aSGreg Roach		// int Cell width (expressed in points) Setting the width to 0 will make it the width from the current location to the right margin.
443a6f13a4aSGreg Roach		$width = 0;
444a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
445a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
446a6f13a4aSGreg Roach		}
447a6f13a4aSGreg Roach
448a6f13a4aSGreg Roach		// int Stretch carachter mode
449a6f13a4aSGreg Roach		$stretch = 0;
450a6f13a4aSGreg Roach		if (!empty($attrs['stretch'])) {
451a6f13a4aSGreg Roach			$stretch = (int) $attrs['stretch'];
452a6f13a4aSGreg Roach		}
453a6f13a4aSGreg Roach
454a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. The default is the current position.
455a6f13a4aSGreg Roach		$left = ".";
456a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
457a6f13a4aSGreg Roach			if ($attrs['left'] === ".") {
458a6f13a4aSGreg Roach				$left = ".";
459a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
460a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
461a6f13a4aSGreg Roach			} elseif ($attrs['left'] === "0") {
462a6f13a4aSGreg Roach				$left = 0;
463a6f13a4aSGreg Roach			}
464a6f13a4aSGreg Roach		}
465a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
466a6f13a4aSGreg Roach		$top = ".";
467a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
468a6f13a4aSGreg Roach			if ($attrs['top'] === ".") {
469a6f13a4aSGreg Roach				$top = ".";
470a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
471a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
472a6f13a4aSGreg Roach			} elseif ($attrs['top'] === "0") {
473a6f13a4aSGreg Roach				$top = 0;
474a6f13a4aSGreg Roach			}
475a6f13a4aSGreg Roach		}
476a6f13a4aSGreg Roach
477a6f13a4aSGreg Roach		// string The name of the Style that should be used to render the text.
478a6f13a4aSGreg Roach		$style = "";
479a6f13a4aSGreg Roach		if (!empty($attrs['style'])) {
480a6f13a4aSGreg Roach			$style = $attrs['style'];
481a6f13a4aSGreg Roach		}
482a6f13a4aSGreg Roach
483a6f13a4aSGreg Roach		// string Text color in html code
484a6f13a4aSGreg Roach		$tcolor = "";
485a6f13a4aSGreg Roach		if (!empty($attrs['tcolor'])) {
486a6f13a4aSGreg Roach			$tcolor = $attrs['tcolor'];
487a6f13a4aSGreg Roach		}
488a6f13a4aSGreg Roach
489a6f13a4aSGreg Roach		// int Indicates where the current position should go after the call.
490a6f13a4aSGreg Roach		$ln = 0;
491a6f13a4aSGreg Roach		if (isset($attrs['newline'])) {
492a6f13a4aSGreg Roach			if (!empty($attrs['newline'])) {
493a6f13a4aSGreg Roach				$ln = (int) $attrs['newline'];
494a6f13a4aSGreg Roach			} elseif ($attrs['newline'] === "0") {
495a6f13a4aSGreg Roach				$ln = 0;
496a6f13a4aSGreg Roach			}
497a6f13a4aSGreg Roach		}
498a6f13a4aSGreg Roach
499a6f13a4aSGreg Roach		if ($align == "left") {
500a6f13a4aSGreg Roach			$align = "L";
501a6f13a4aSGreg Roach		} elseif ($align == "right") {
502a6f13a4aSGreg Roach			$align = "R";
503a6f13a4aSGreg Roach		} elseif ($align == "center") {
504a6f13a4aSGreg Roach			$align = "C";
505a6f13a4aSGreg Roach		} elseif ($align == "justify") {
506a6f13a4aSGreg Roach			$align = "J";
507a6f13a4aSGreg Roach		}
508a6f13a4aSGreg Roach
509a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
510a6f13a4aSGreg Roach		$this->print_data = true;
511a6f13a4aSGreg Roach
512a6f13a4aSGreg Roach		$this->current_element = $ReportRoot->createCell(
513a6f13a4aSGreg Roach			$width,
514a6f13a4aSGreg Roach			$height,
515a6f13a4aSGreg Roach			$border,
516a6f13a4aSGreg Roach			$align,
517a6f13a4aSGreg Roach			$bgcolor,
518a6f13a4aSGreg Roach			$style,
519a6f13a4aSGreg Roach			$ln,
520a6f13a4aSGreg Roach			$top,
521a6f13a4aSGreg Roach			$left,
522a6f13a4aSGreg Roach			$fill,
523a6f13a4aSGreg Roach			$stretch,
524a6f13a4aSGreg Roach			$bocolor,
525a6f13a4aSGreg Roach			$tcolor,
526a6f13a4aSGreg Roach			$reseth
527a6f13a4aSGreg Roach		);
528a6f13a4aSGreg Roach	}
529a6f13a4aSGreg Roach
530a6f13a4aSGreg Roach	/**
531a6f13a4aSGreg Roach	 * XML </Cell> end element handler
532a6f13a4aSGreg Roach	 */
533*8edd1043SGreg Roach	private function cellEndHandler() {
534a6f13a4aSGreg Roach		global $wt_report;
535a6f13a4aSGreg Roach
536a6f13a4aSGreg Roach		$this->print_data = array_pop($this->print_data_stack);
537a6f13a4aSGreg Roach		$wt_report->addElement($this->current_element);
538a6f13a4aSGreg Roach	}
539a6f13a4aSGreg Roach
540a6f13a4aSGreg Roach	/**
541a6f13a4aSGreg Roach	 * XML <Now /> element handler
542a6f13a4aSGreg Roach	 */
543*8edd1043SGreg Roach	private function nowStartHandler() {
544a6f13a4aSGreg Roach		$g = timestamp_to_gedcom_date(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
545a6f13a4aSGreg Roach		$this->current_element->addText($g->display());
546a6f13a4aSGreg Roach	}
547a6f13a4aSGreg Roach
548a6f13a4aSGreg Roach	/**
549a6f13a4aSGreg Roach	 * XML <PageNum /> element handler
550a6f13a4aSGreg Roach	 */
551*8edd1043SGreg Roach	private function pageNumStartHandler() {
552a6f13a4aSGreg Roach		$this->current_element->addText("#PAGENUM#");
553a6f13a4aSGreg Roach	}
554a6f13a4aSGreg Roach
555a6f13a4aSGreg Roach	/**
556a6f13a4aSGreg Roach	 * XML <TotalPages /> element handler
557a6f13a4aSGreg Roach	 */
558*8edd1043SGreg Roach	private function totalPagesStartHandler() {
559a6f13a4aSGreg Roach		$this->current_element->addText("{{:ptp:}}");
560a6f13a4aSGreg Roach	}
561a6f13a4aSGreg Roach
562a6f13a4aSGreg Roach	/**
563a6f13a4aSGreg Roach	 * Called at the start of an element.
564a6f13a4aSGreg Roach	 *
565a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
566a6f13a4aSGreg Roach	 */
567*8edd1043SGreg Roach	private function gedcomStartHandler($attrs) {
568a6f13a4aSGreg Roach		global $vars, $WT_TREE;
569a6f13a4aSGreg Roach
570a6f13a4aSGreg Roach		if ($this->process_gedcoms > 0) {
571a6f13a4aSGreg Roach			$this->process_gedcoms++;
572a6f13a4aSGreg Roach
573a6f13a4aSGreg Roach			return;
574a6f13a4aSGreg Roach		}
575a6f13a4aSGreg Roach
576a6f13a4aSGreg Roach		$tag       = $attrs['id'];
577a6f13a4aSGreg Roach		$tag       = str_replace("@fact", $this->fact, $tag);
578a6f13a4aSGreg Roach		$tags      = explode(":", $tag);
579a6f13a4aSGreg Roach		$newgedrec = '';
580a6f13a4aSGreg Roach		if (count($tags) < 2) {
581a6f13a4aSGreg Roach			$tmp       = GedcomRecord::getInstance($attrs['id'], $WT_TREE);
582a6f13a4aSGreg Roach			$newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
583a6f13a4aSGreg Roach		}
584a6f13a4aSGreg Roach		if (empty($newgedrec)) {
585a6f13a4aSGreg Roach			$tgedrec   = $this->gedrec;
586a6f13a4aSGreg Roach			$newgedrec = '';
587a6f13a4aSGreg Roach			foreach ($tags as $tag) {
588a6f13a4aSGreg Roach				if (preg_match("/\\$(.+)/", $tag, $match)) {
589a6f13a4aSGreg Roach					if (isset($vars[$match[1]]['gedcom'])) {
590a6f13a4aSGreg Roach						$newgedrec = $vars[$match[1]]['gedcom'];
591a6f13a4aSGreg Roach					} else {
592a6f13a4aSGreg Roach						$tmp       = GedcomRecord::getInstance($match[1], $WT_TREE);
593a6f13a4aSGreg Roach						$newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
594a6f13a4aSGreg Roach					}
595a6f13a4aSGreg Roach				} else {
596a6f13a4aSGreg Roach					if (preg_match("/@(.+)/", $tag, $match)) {
597a6f13a4aSGreg Roach						$gmatch = array();
598a6f13a4aSGreg Roach						if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) {
599a6f13a4aSGreg Roach							$tmp       = GedcomRecord::getInstance($gmatch[1], $WT_TREE);
600a6f13a4aSGreg Roach							$newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
601a6f13a4aSGreg Roach							$tgedrec   = $newgedrec;
602a6f13a4aSGreg Roach						} else {
603a6f13a4aSGreg Roach							$newgedrec = '';
604a6f13a4aSGreg Roach							break;
605a6f13a4aSGreg Roach						}
606a6f13a4aSGreg Roach					} else {
607a6f13a4aSGreg Roach						$temp      = explode(" ", trim($tgedrec));
608a6f13a4aSGreg Roach						$level     = $temp[0] + 1;
609a6f13a4aSGreg Roach						$newgedrec = get_sub_record($level, "$level $tag", $tgedrec);
610a6f13a4aSGreg Roach						$tgedrec   = $newgedrec;
611a6f13a4aSGreg Roach					}
612a6f13a4aSGreg Roach				}
613a6f13a4aSGreg Roach			}
614a6f13a4aSGreg Roach		}
615a6f13a4aSGreg Roach		if (!empty($newgedrec)) {
616a6f13a4aSGreg Roach			array_push($this->gedrec_stack, array($this->gedrec, $this->fact, $this->desc));
617a6f13a4aSGreg Roach			$this->gedrec = $newgedrec;
618a6f13a4aSGreg Roach			if (preg_match("/(\d+) (_?[A-Z0-9]+) (.*)/", $this->gedrec, $match)) {
619a6f13a4aSGreg Roach				$this->fact = $match[2];
620a6f13a4aSGreg Roach				$this->desc = trim($match[3]);
621a6f13a4aSGreg Roach			}
622a6f13a4aSGreg Roach		} else {
623a6f13a4aSGreg Roach			$this->process_gedcoms++;
624a6f13a4aSGreg Roach		}
625a6f13a4aSGreg Roach	}
626a6f13a4aSGreg Roach
627a6f13a4aSGreg Roach	/**
628a6f13a4aSGreg Roach	 * Called at the end of an element.
629a6f13a4aSGreg Roach	 */
630*8edd1043SGreg Roach	private function gedcomEndHandler() {
631a6f13a4aSGreg Roach		if ($this->process_gedcoms > 0) {
632a6f13a4aSGreg Roach			$this->process_gedcoms--;
633a6f13a4aSGreg Roach		} else {
634a6f13a4aSGreg Roach			list($this->gedrec, $this->fact, $this->desc) = array_pop($this->gedrec_stack);
635a6f13a4aSGreg Roach		}
636a6f13a4aSGreg Roach	}
637a6f13a4aSGreg Roach
638a6f13a4aSGreg Roach	/**
639a6f13a4aSGreg Roach	 * XML <textBoxStartHandler> start element handler
640a6f13a4aSGreg Roach	 *
641a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
642a6f13a4aSGreg Roach	 */
643*8edd1043SGreg Roach	private function textBoxStartHandler($attrs) {
644a6f13a4aSGreg Roach		global $wt_report, $wt_reportStack, $ReportRoot;
645a6f13a4aSGreg Roach
646a6f13a4aSGreg Roach		// string Background color code
647a6f13a4aSGreg Roach		$bgcolor = "";
648a6f13a4aSGreg Roach		if (!empty($attrs['bgcolor'])) {
649a6f13a4aSGreg Roach			$bgcolor = $attrs['bgcolor'];
650a6f13a4aSGreg Roach		}
651a6f13a4aSGreg Roach
652a6f13a4aSGreg Roach		// boolean Wether or not fill the background color
653a6f13a4aSGreg Roach		$fill = true;
654a6f13a4aSGreg Roach		if (isset($attrs['fill'])) {
655a6f13a4aSGreg Roach			if ($attrs['fill'] === "0") {
656a6f13a4aSGreg Roach				$fill = false;
657a6f13a4aSGreg Roach			} elseif ($attrs['fill'] === "1") {
658a6f13a4aSGreg Roach				$fill = true;
659a6f13a4aSGreg Roach			}
660a6f13a4aSGreg Roach		}
661a6f13a4aSGreg Roach
662a6f13a4aSGreg Roach		// var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
663a6f13a4aSGreg Roach		$border = false;
664a6f13a4aSGreg Roach		if (isset($attrs['border'])) {
665a6f13a4aSGreg Roach			if ($attrs['border'] === "1") {
666a6f13a4aSGreg Roach				$border = true;
667a6f13a4aSGreg Roach			} elseif ($attrs['border'] === "0") {
668a6f13a4aSGreg Roach				$border = false;
669a6f13a4aSGreg Roach			}
670a6f13a4aSGreg Roach		}
671a6f13a4aSGreg Roach
672a6f13a4aSGreg Roach		// int The starting height of this cell. If the text wraps the height will automatically be adjusted
673a6f13a4aSGreg Roach		$height = 0;
674a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
675a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
676a6f13a4aSGreg Roach		}
677a6f13a4aSGreg Roach		// int Setting the width to 0 will make it the width from the current location to the margin
678a6f13a4aSGreg Roach		$width = 0;
679a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
680a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
681a6f13a4aSGreg Roach		}
682a6f13a4aSGreg Roach
683a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. The default is the current position.
684a6f13a4aSGreg Roach		$left = ".";
685a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
686a6f13a4aSGreg Roach			if ($attrs['left'] === ".") {
687a6f13a4aSGreg Roach				$left = ".";
688a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
689a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
690a6f13a4aSGreg Roach			} elseif ($attrs['left'] === "0") {
691a6f13a4aSGreg Roach				$left = 0;
692a6f13a4aSGreg Roach			}
693a6f13a4aSGreg Roach		}
694a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
695a6f13a4aSGreg Roach		$top = ".";
696a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
697a6f13a4aSGreg Roach			if ($attrs['top'] === ".") {
698a6f13a4aSGreg Roach				$top = ".";
699a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
700a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
701a6f13a4aSGreg Roach			} elseif ($attrs['top'] === "0") {
702a6f13a4aSGreg Roach				$top = 0;
703a6f13a4aSGreg Roach			}
704a6f13a4aSGreg Roach		}
705a6f13a4aSGreg Roach		// boolean After this box is finished rendering, should the next section of text start immediately after the this box or should it start on a new line under this box. 0 = no new line, 1 = force new line. Default is 0
706a6f13a4aSGreg Roach		$newline = false;
707a6f13a4aSGreg Roach		if (isset($attrs['newline'])) {
708a6f13a4aSGreg Roach			if ($attrs['newline'] === "1") {
709a6f13a4aSGreg Roach				$newline = true;
710a6f13a4aSGreg Roach			} elseif ($attrs['newline'] === "0") {
711a6f13a4aSGreg Roach				$newline = false;
712a6f13a4aSGreg Roach			}
713a6f13a4aSGreg Roach		}
714a6f13a4aSGreg Roach		// boolean
715a6f13a4aSGreg Roach		$pagecheck = true;
716a6f13a4aSGreg Roach		if (isset($attrs['pagecheck'])) {
717a6f13a4aSGreg Roach			if ($attrs['pagecheck'] === "0") {
718a6f13a4aSGreg Roach				$pagecheck = false;
719a6f13a4aSGreg Roach			} elseif ($attrs['pagecheck'] === "1") {
720a6f13a4aSGreg Roach				$pagecheck = true;
721a6f13a4aSGreg Roach			}
722a6f13a4aSGreg Roach		}
723a6f13a4aSGreg Roach		// boolean Cell padding
724a6f13a4aSGreg Roach		$padding = true;
725a6f13a4aSGreg Roach		if (isset($attrs['padding'])) {
726a6f13a4aSGreg Roach			if ($attrs['padding'] === "0") {
727a6f13a4aSGreg Roach				$padding = false;
728a6f13a4aSGreg Roach			} elseif ($attrs['padding'] === "1") {
729a6f13a4aSGreg Roach				$padding = true;
730a6f13a4aSGreg Roach			}
731a6f13a4aSGreg Roach		}
732a6f13a4aSGreg Roach		// boolean Reset this box Height
733a6f13a4aSGreg Roach		$reseth = false;
734a6f13a4aSGreg Roach		if (isset($attrs['reseth'])) {
735a6f13a4aSGreg Roach			if ($attrs['reseth'] === "1") {
736a6f13a4aSGreg Roach				$reseth = true;
737a6f13a4aSGreg Roach			} elseif ($attrs['reseth'] === "0") {
738a6f13a4aSGreg Roach				$reseth = false;
739a6f13a4aSGreg Roach			}
740a6f13a4aSGreg Roach		}
741a6f13a4aSGreg Roach
742a6f13a4aSGreg Roach		// string Style of rendering
743a6f13a4aSGreg Roach		$style = "";
744a6f13a4aSGreg Roach
745a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
746a6f13a4aSGreg Roach		$this->print_data = false;
747a6f13a4aSGreg Roach
748a6f13a4aSGreg Roach		array_push($wt_reportStack, $wt_report);
749a6f13a4aSGreg Roach		$wt_report = $ReportRoot->createTextBox(
750a6f13a4aSGreg Roach			$width,
751a6f13a4aSGreg Roach			$height,
752a6f13a4aSGreg Roach			$border,
753a6f13a4aSGreg Roach			$bgcolor,
754a6f13a4aSGreg Roach			$newline,
755a6f13a4aSGreg Roach			$left,
756a6f13a4aSGreg Roach			$top,
757a6f13a4aSGreg Roach			$pagecheck,
758a6f13a4aSGreg Roach			$style,
759a6f13a4aSGreg Roach			$fill,
760a6f13a4aSGreg Roach			$padding,
761a6f13a4aSGreg Roach			$reseth
762a6f13a4aSGreg Roach		);
763a6f13a4aSGreg Roach	}
764a6f13a4aSGreg Roach
765a6f13a4aSGreg Roach	/**
766a6f13a4aSGreg Roach	 * XML <textBoxEndHandler> end element handler
767a6f13a4aSGreg Roach	 */
768*8edd1043SGreg Roach	private function textBoxEndHandler() {
769a6f13a4aSGreg Roach		global $wt_report, $wt_reportStack;
770a6f13a4aSGreg Roach
771a6f13a4aSGreg Roach		$this->print_data      = array_pop($this->print_data_stack);
772a6f13a4aSGreg Roach		$this->current_element = $wt_report;
773a6f13a4aSGreg Roach		$wt_report             = array_pop($wt_reportStack);
774a6f13a4aSGreg Roach		$wt_report->addElement($this->current_element);
775a6f13a4aSGreg Roach	}
776a6f13a4aSGreg Roach
777a6f13a4aSGreg Roach	/**
778a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
779a6f13a4aSGreg Roach	 */
780*8edd1043SGreg Roach	private function textStartHandler($attrs) {
781a6f13a4aSGreg Roach		global $ReportRoot;
782a6f13a4aSGreg Roach
783a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
784a6f13a4aSGreg Roach		$this->print_data = true;
785a6f13a4aSGreg Roach
786a6f13a4aSGreg Roach		// string The name of the Style that should be used to render the text.
787a6f13a4aSGreg Roach		$style = "";
788a6f13a4aSGreg Roach		if (!empty($attrs['style'])) {
789a6f13a4aSGreg Roach			$style = $attrs['style'];
790a6f13a4aSGreg Roach		}
791a6f13a4aSGreg Roach
792a6f13a4aSGreg Roach		// string  The color of the text - Keep the black color as default
793a6f13a4aSGreg Roach		$color = "";
794a6f13a4aSGreg Roach		if (!empty($attrs['color'])) {
795a6f13a4aSGreg Roach			$color = $attrs['color'];
796a6f13a4aSGreg Roach		}
797a6f13a4aSGreg Roach
798a6f13a4aSGreg Roach		$this->current_element = $ReportRoot->createText($style, $color);
799a6f13a4aSGreg Roach	}
800a6f13a4aSGreg Roach
801a6f13a4aSGreg Roach	/**
802a6f13a4aSGreg Roach	 *
803a6f13a4aSGreg Roach	 */
804*8edd1043SGreg Roach	private function textEndHandler() {
805a6f13a4aSGreg Roach		global $wt_report;
806a6f13a4aSGreg Roach
807a6f13a4aSGreg Roach		$this->print_data = array_pop($this->print_data_stack);
808a6f13a4aSGreg Roach		$wt_report->addElement($this->current_element);
809a6f13a4aSGreg Roach	}
810a6f13a4aSGreg Roach
811a6f13a4aSGreg Roach	/**
812a6f13a4aSGreg Roach	 * XML <GetPersonName> start element handler
813a6f13a4aSGreg Roach	 * Get the name
814a6f13a4aSGreg Roach	 * 1. id is empty - current GEDCOM record
815a6f13a4aSGreg Roach	 * 2. id is set with a record id
816a6f13a4aSGreg Roach	 *
817a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
818a6f13a4aSGreg Roach	 */
819*8edd1043SGreg Roach	private function getPersonNameStartHandler($attrs) {
820a6f13a4aSGreg Roach		global $vars, $WT_TREE;
821a6f13a4aSGreg Roach
822a6f13a4aSGreg Roach		$id    = "";
823a6f13a4aSGreg Roach		$match = array();
824a6f13a4aSGreg Roach		if (empty($attrs['id'])) {
825a6f13a4aSGreg Roach			if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
826a6f13a4aSGreg Roach				$id = $match[1];
827a6f13a4aSGreg Roach			}
828a6f13a4aSGreg Roach		} else {
829a6f13a4aSGreg Roach			if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
830a6f13a4aSGreg Roach				if (isset($vars[$match[1]]['id'])) {
831a6f13a4aSGreg Roach					$id = $vars[$match[1]]['id'];
832a6f13a4aSGreg Roach				}
833a6f13a4aSGreg Roach			} else {
834a6f13a4aSGreg Roach				if (preg_match("/@(.+)/", $attrs['id'], $match)) {
835a6f13a4aSGreg Roach					$gmatch = array();
836a6f13a4aSGreg Roach					if (preg_match("/\d $match[1] @([^@]+)@/", $this->gedrec, $gmatch)) {
837a6f13a4aSGreg Roach						$id = $gmatch[1];
838a6f13a4aSGreg Roach					}
839a6f13a4aSGreg Roach				} else {
840a6f13a4aSGreg Roach					$id = $attrs['id'];
841a6f13a4aSGreg Roach				}
842a6f13a4aSGreg Roach			}
843a6f13a4aSGreg Roach		}
844a6f13a4aSGreg Roach		if (!empty($id)) {
845a6f13a4aSGreg Roach			$record = GedcomRecord::getInstance($id, $WT_TREE);
846a6f13a4aSGreg Roach			if (is_null($record)) {
847a6f13a4aSGreg Roach				return;
848a6f13a4aSGreg Roach			}
849a6f13a4aSGreg Roach			if (!$record->canShowName()) {
850a6f13a4aSGreg Roach				$this->current_element->addText(I18N::translate('Private'));
851a6f13a4aSGreg Roach			} else {
852a6f13a4aSGreg Roach				$name = $record->getFullName();
853a6f13a4aSGreg Roach				$name = preg_replace(
854a6f13a4aSGreg Roach					array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
855a6f13a4aSGreg Roach					array('«', '', '»'),
856a6f13a4aSGreg Roach					$name
857a6f13a4aSGreg Roach				);
858a6f13a4aSGreg Roach				$name = strip_tags($name);
859a6f13a4aSGreg Roach				if (!empty($attrs['truncate'])) {
860a6f13a4aSGreg Roach					if (mb_strlen($name) > $attrs['truncate']) {
861a6f13a4aSGreg Roach						$name  = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
862a6f13a4aSGreg Roach						$words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
863a6f13a4aSGreg Roach						$name  = $words[count($words) - 1];
864a6f13a4aSGreg Roach						for ($i = count($words) - 2; $i >= 0; $i--) {
865a6f13a4aSGreg Roach							$len = mb_strlen($name);
866a6f13a4aSGreg Roach							for ($j = count($words) - 3; $j >= 0; $j--) {
867a6f13a4aSGreg Roach								$len += mb_strlen($words[$j]);
868a6f13a4aSGreg Roach							}
869a6f13a4aSGreg Roach							if ($len > $attrs['truncate']) {
870a6f13a4aSGreg Roach								$first_letter = mb_substr($words[$i], 0, 1);
871a6f13a4aSGreg Roach								// Do not show " of nick-names
872a6f13a4aSGreg Roach								if ($first_letter != "\"") {
873a6f13a4aSGreg Roach									$name = mb_substr($words[$i], 0, 1) . '. ' . $name;
874a6f13a4aSGreg Roach								}
875a6f13a4aSGreg Roach							} else {
876a6f13a4aSGreg Roach								$name = $words[$i] . ' ' . $name;
877a6f13a4aSGreg Roach							}
878a6f13a4aSGreg Roach						}
879a6f13a4aSGreg Roach					}
880a6f13a4aSGreg Roach				} else {
881a6f13a4aSGreg Roach					$addname = $record->getAddName();
882a6f13a4aSGreg Roach					$addname = preg_replace(
883a6f13a4aSGreg Roach						array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
884a6f13a4aSGreg Roach						array('«', '', '»'),
885a6f13a4aSGreg Roach						$addname
886a6f13a4aSGreg Roach					);
887a6f13a4aSGreg Roach					$addname = strip_tags($addname);
888a6f13a4aSGreg Roach					if (!empty($addname)) {
889a6f13a4aSGreg Roach						$name .= " " . $addname;
890a6f13a4aSGreg Roach					}
891a6f13a4aSGreg Roach				}
892a6f13a4aSGreg Roach				$this->current_element->addText(trim($name));
893a6f13a4aSGreg Roach			}
894a6f13a4aSGreg Roach		}
895a6f13a4aSGreg Roach	}
896a6f13a4aSGreg Roach
897a6f13a4aSGreg Roach	/**
898a6f13a4aSGreg Roach	 * XML <GedcomValue> start element handler
899a6f13a4aSGreg Roach	 *
900a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
901a6f13a4aSGreg Roach	 */
902*8edd1043SGreg Roach	private function gedcomValueStartHandler($attrs) {
903a6f13a4aSGreg Roach		global $WT_TREE;
904a6f13a4aSGreg Roach
905a6f13a4aSGreg Roach		$id    = "";
906a6f13a4aSGreg Roach		$match = array();
907a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
908a6f13a4aSGreg Roach			$id = $match[1];
909a6f13a4aSGreg Roach		}
910a6f13a4aSGreg Roach
911a6f13a4aSGreg Roach		if (isset($attrs['newline']) && $attrs['newline'] == "1") {
912a6f13a4aSGreg Roach			$useBreak = "1";
913a6f13a4aSGreg Roach		} else {
914a6f13a4aSGreg Roach			$useBreak = "0";
915a6f13a4aSGreg Roach		}
916a6f13a4aSGreg Roach
917a6f13a4aSGreg Roach		$tag = $attrs['tag'];
918a6f13a4aSGreg Roach		if (!empty($tag)) {
919a6f13a4aSGreg Roach			if ($tag == "@desc") {
920a6f13a4aSGreg Roach				$value = $this->desc;
921a6f13a4aSGreg Roach				$value = trim($value);
922a6f13a4aSGreg Roach				$this->current_element->addText($value);
923a6f13a4aSGreg Roach			}
924a6f13a4aSGreg Roach			if ($tag == "@id") {
925a6f13a4aSGreg Roach				$this->current_element->addText($id);
926a6f13a4aSGreg Roach			} else {
927a6f13a4aSGreg Roach				$tag = str_replace("@fact", $this->fact, $tag);
928a6f13a4aSGreg Roach				if (empty($attrs['level'])) {
929a6f13a4aSGreg Roach					$temp  = explode(" ", trim($this->gedrec));
930a6f13a4aSGreg Roach					$level = $temp[0];
931a6f13a4aSGreg Roach					if ($level == 0) {
932a6f13a4aSGreg Roach						$level++;
933a6f13a4aSGreg Roach					}
934a6f13a4aSGreg Roach				} else {
935a6f13a4aSGreg Roach					$level = $attrs['level'];
936a6f13a4aSGreg Roach				}
937a6f13a4aSGreg Roach				$tags  = preg_split('/[: ]/', $tag);
938a6f13a4aSGreg Roach				$value = get_gedcom_value($tag, $level, $this->gedrec);
939a6f13a4aSGreg Roach				switch (end($tags)) {
940a6f13a4aSGreg Roach					case 'DATE':
941a6f13a4aSGreg Roach						$tmp   = new Date($value);
942a6f13a4aSGreg Roach						$value = $tmp->display();
943a6f13a4aSGreg Roach						break;
944a6f13a4aSGreg Roach					case 'PLAC':
945a6f13a4aSGreg Roach						$tmp   = new Place($value, $WT_TREE);
946a6f13a4aSGreg Roach						$value = $tmp->getShortName();
947a6f13a4aSGreg Roach						break;
948a6f13a4aSGreg Roach				}
949a6f13a4aSGreg Roach				if ($useBreak == "1") {
950a6f13a4aSGreg Roach					// Insert <br> when multiple dates exist.
951a6f13a4aSGreg Roach					// This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
952a6f13a4aSGreg Roach					$value = str_replace('(', '<br>(', $value);
953a6f13a4aSGreg Roach					$value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
954a6f13a4aSGreg Roach					$value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
955a6f13a4aSGreg Roach					if (substr($value, 0, 6) == '<br>') {
956a6f13a4aSGreg Roach						$value = substr($value, 6);
957a6f13a4aSGreg Roach					}
958a6f13a4aSGreg Roach				}
959a6f13a4aSGreg Roach				$this->current_element->addText($value);
960a6f13a4aSGreg Roach			}
961a6f13a4aSGreg Roach		}
962a6f13a4aSGreg Roach	}
963a6f13a4aSGreg Roach
964a6f13a4aSGreg Roach	/**
965a6f13a4aSGreg Roach	 * XML <RepeatTag> start element handler
966a6f13a4aSGreg Roach	 *
967a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
968a6f13a4aSGreg Roach	 */
969*8edd1043SGreg Roach	private function repeatTagStartHandler($attrs) {
970a6f13a4aSGreg Roach		global $parser;
971a6f13a4aSGreg Roach
972a6f13a4aSGreg Roach		$this->process_repeats++;
973a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
974a6f13a4aSGreg Roach			return;
975a6f13a4aSGreg Roach		}
976a6f13a4aSGreg Roach
977a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
978a6f13a4aSGreg Roach		$this->repeats      = array();
979a6f13a4aSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($parser);
980a6f13a4aSGreg Roach
981a6f13a4aSGreg Roach		$tag = "";
982a6f13a4aSGreg Roach		if (isset($attrs['tag'])) {
983a6f13a4aSGreg Roach			$tag = $attrs['tag'];
984a6f13a4aSGreg Roach		}
985a6f13a4aSGreg Roach		if (!empty($tag)) {
986a6f13a4aSGreg Roach			if ($tag == "@desc") {
987a6f13a4aSGreg Roach				$value = $this->desc;
988a6f13a4aSGreg Roach				$value = trim($value);
989a6f13a4aSGreg Roach				$this->current_element->addText($value);
990a6f13a4aSGreg Roach			} else {
991a6f13a4aSGreg Roach				$tag   = str_replace("@fact", $this->fact, $tag);
992a6f13a4aSGreg Roach				$tags  = explode(":", $tag);
993a6f13a4aSGreg Roach				$temp  = explode(" ", trim($this->gedrec));
994a6f13a4aSGreg Roach				$level = $temp[0];
995a6f13a4aSGreg Roach				if ($level == 0) {
996a6f13a4aSGreg Roach					$level++;
997a6f13a4aSGreg Roach				}
998a6f13a4aSGreg Roach				$subrec = $this->gedrec;
999a6f13a4aSGreg Roach				$t      = $tag;
1000a6f13a4aSGreg Roach				$count  = count($tags);
1001a6f13a4aSGreg Roach				$i      = 0;
1002a6f13a4aSGreg Roach				while ($i < $count) {
1003a6f13a4aSGreg Roach					$t = $tags[$i];
1004a6f13a4aSGreg Roach					if (!empty($t)) {
1005a6f13a4aSGreg Roach						if ($i < ($count - 1)) {
1006a6f13a4aSGreg Roach							$subrec = get_sub_record($level, "$level $t", $subrec);
1007a6f13a4aSGreg Roach							if (empty($subrec)) {
1008a6f13a4aSGreg Roach								$level--;
1009a6f13a4aSGreg Roach								$subrec = get_sub_record($level, "@ $t", $this->gedrec);
1010a6f13a4aSGreg Roach								if (empty($subrec)) {
1011a6f13a4aSGreg Roach									return;
1012a6f13a4aSGreg Roach								}
1013a6f13a4aSGreg Roach							}
1014a6f13a4aSGreg Roach						}
1015a6f13a4aSGreg Roach						$level++;
1016a6f13a4aSGreg Roach					}
1017a6f13a4aSGreg Roach					$i++;
1018a6f13a4aSGreg Roach				}
1019a6f13a4aSGreg Roach				$level--;
1020a6f13a4aSGreg Roach				$count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
1021a6f13a4aSGreg Roach				$i     = 0;
1022a6f13a4aSGreg Roach				while ($i < $count) {
1023a6f13a4aSGreg Roach					$this->repeats[] = get_sub_record($level, "$level $t", $subrec, $i + 1);
1024a6f13a4aSGreg Roach					$i++;
1025a6f13a4aSGreg Roach				}
1026a6f13a4aSGreg Roach			}
1027a6f13a4aSGreg Roach		}
1028a6f13a4aSGreg Roach	}
1029a6f13a4aSGreg Roach
1030a6f13a4aSGreg Roach	/**
1031a6f13a4aSGreg Roach	 * XML </ RepeatTag> end element handler
1032a6f13a4aSGreg Roach	 */
1033*8edd1043SGreg Roach	private function repeatTagEndHandler() {
1034a6f13a4aSGreg Roach		global $parser, $parserStack, $report;
1035a6f13a4aSGreg Roach
1036a6f13a4aSGreg Roach		$this->process_repeats--;
1037a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
1038a6f13a4aSGreg Roach			return;
1039a6f13a4aSGreg Roach		}
1040a6f13a4aSGreg Roach
1041a6f13a4aSGreg Roach		// Check if there is anything to repeat
1042a6f13a4aSGreg Roach		if (count($this->repeats) > 0) {
1043a6f13a4aSGreg Roach			// No need to load them if not used...
1044a6f13a4aSGreg Roach
1045a6f13a4aSGreg Roach			$lineoffset = 0;
1046a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
1047a6f13a4aSGreg Roach				$lineoffset += $rep[1];
1048a6f13a4aSGreg Roach			}
1049a6f13a4aSGreg Roach			//-- read the xml from the file
1050a6f13a4aSGreg Roach			$lines = file($report);
1051a6f13a4aSGreg Roach			while (strpos($lines[$lineoffset + $this->repeat_bytes], "<RepeatTag") === false) {
1052a6f13a4aSGreg Roach				$lineoffset--;
1053a6f13a4aSGreg Roach			}
1054a6f13a4aSGreg Roach			$lineoffset++;
1055a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
1056a6f13a4aSGreg Roach			$line_nr   = $lineoffset + $this->repeat_bytes;
1057a6f13a4aSGreg Roach			// RepeatTag Level counter
1058a6f13a4aSGreg Roach			$count = 1;
1059a6f13a4aSGreg Roach			while (0 < $count) {
1060a6f13a4aSGreg Roach				if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
1061a6f13a4aSGreg Roach					$count++;
1062a6f13a4aSGreg Roach				} elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
1063a6f13a4aSGreg Roach					$count--;
1064a6f13a4aSGreg Roach				}
1065a6f13a4aSGreg Roach				if (0 < $count) {
1066a6f13a4aSGreg Roach					$reportxml .= $lines[$line_nr];
1067a6f13a4aSGreg Roach				}
1068a6f13a4aSGreg Roach				$line_nr++;
1069a6f13a4aSGreg Roach			}
1070a6f13a4aSGreg Roach			// No need to drag this
1071a6f13a4aSGreg Roach			unset($lines);
1072a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>\n";
1073a6f13a4aSGreg Roach			// Save original values
1074a6f13a4aSGreg Roach			array_push($parserStack, $parser);
1075a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
1076a6f13a4aSGreg Roach			foreach ($this->repeats as $gedrec) {
1077a6f13a4aSGreg Roach				$this->gedrec  = $gedrec;
1078a6f13a4aSGreg Roach				$repeat_parser = xml_parser_create();
1079a6f13a4aSGreg Roach				$parser        = $repeat_parser;
1080a6f13a4aSGreg Roach				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1081a6f13a4aSGreg Roach				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1082a6f13a4aSGreg Roach				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1083a6f13a4aSGreg Roach				if (!xml_parse($repeat_parser, $reportxml, true)) {
1084a6f13a4aSGreg Roach					throw new \DomainException(sprintf(
1085a6f13a4aSGreg Roach						'RepeatTagEHandler XML error: %s at line %d',
1086a6f13a4aSGreg Roach						xml_error_string(xml_get_error_code($repeat_parser)),
1087a6f13a4aSGreg Roach						xml_get_current_line_number($repeat_parser)
1088a6f13a4aSGreg Roach					));
1089a6f13a4aSGreg Roach				}
1090a6f13a4aSGreg Roach				xml_parser_free($repeat_parser);
1091a6f13a4aSGreg Roach			}
1092a6f13a4aSGreg Roach			// Restore original values
1093a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
1094a6f13a4aSGreg Roach			$parser = array_pop($parserStack);
1095a6f13a4aSGreg Roach		}
1096a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1097a6f13a4aSGreg Roach	}
1098a6f13a4aSGreg Roach
1099a6f13a4aSGreg Roach	/**
1100a6f13a4aSGreg Roach	 * Variable lookup
1101a6f13a4aSGreg Roach	 *
1102a6f13a4aSGreg Roach	 * Retrieve predefined variables :
1103a6f13a4aSGreg Roach	 *
1104a6f13a4aSGreg Roach	 * @ desc GEDCOM fact description, example:
1105a6f13a4aSGreg Roach	 *        1 EVEN This is a description
1106a6f13a4aSGreg Roach	 * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
1107a6f13a4aSGreg Roach	 * $ I18N::translate('....')
1108a6f13a4aSGreg Roach	 * $ language_settings[]
1109a6f13a4aSGreg Roach	 *
1110a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1111a6f13a4aSGreg Roach	 */
1112*8edd1043SGreg Roach	private function varStartHandler($attrs) {
1113a6f13a4aSGreg Roach		global $parser, $vars;
1114a6f13a4aSGreg Roach
1115a6f13a4aSGreg Roach		if (empty($attrs['var'])) {
1116a6f13a4aSGreg Roach			throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($parser));
1117a6f13a4aSGreg Roach		}
1118a6f13a4aSGreg Roach
1119a6f13a4aSGreg Roach		$var = $attrs['var'];
1120a6f13a4aSGreg Roach		// SetVar element preset variables
1121a6f13a4aSGreg Roach		if (!empty($vars[$var]['id'])) {
1122a6f13a4aSGreg Roach			$var = $vars[$var]['id'];
1123a6f13a4aSGreg Roach		} else {
1124a6f13a4aSGreg Roach			$tfact = $this->fact;
1125a6f13a4aSGreg Roach			if (($this->fact === "EVEN" || $this->fact === "FACT") && $this->type !== " ") {
1126a6f13a4aSGreg Roach				// Use :
1127a6f13a4aSGreg Roach				// n TYPE This text if string
1128a6f13a4aSGreg Roach				$tfact = $this->type;
1129a6f13a4aSGreg Roach			}
1130a6f13a4aSGreg Roach			$var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $this->desc), $var);
1131a6f13a4aSGreg Roach			if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
1132a6f13a4aSGreg Roach				$var = I18N::number($match[1]);
1133a6f13a4aSGreg Roach			} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
1134a6f13a4aSGreg Roach				$var = I18N::translate($match[1]);
1135a6f13a4aSGreg Roach			} elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
1136a6f13a4aSGreg Roach				$var = I18N::translateContext($match[1], $match[2]);
1137a6f13a4aSGreg Roach			}
1138a6f13a4aSGreg Roach		}
1139a6f13a4aSGreg Roach		// Check if variable is set as a date and reformat the date
1140a6f13a4aSGreg Roach		if (isset($attrs['date'])) {
1141a6f13a4aSGreg Roach			if ($attrs['date'] === "1") {
1142a6f13a4aSGreg Roach				$g   = new Date($var);
1143a6f13a4aSGreg Roach				$var = $g->display();
1144a6f13a4aSGreg Roach			}
1145a6f13a4aSGreg Roach		}
1146a6f13a4aSGreg Roach		$this->current_element->addText($var);
1147a6f13a4aSGreg Roach	}
1148a6f13a4aSGreg Roach
1149a6f13a4aSGreg Roach	/**
1150a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1151a6f13a4aSGreg Roach	 */
1152*8edd1043SGreg Roach	private function factsStartHandler($attrs) {
1153a6f13a4aSGreg Roach		global $parser, $vars, $WT_TREE;
1154a6f13a4aSGreg Roach
1155a6f13a4aSGreg Roach		$this->process_repeats++;
1156a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
1157a6f13a4aSGreg Roach			return;
1158a6f13a4aSGreg Roach		}
1159a6f13a4aSGreg Roach
1160a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
1161a6f13a4aSGreg Roach		$this->repeats      = array();
1162a6f13a4aSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($parser);
1163a6f13a4aSGreg Roach
1164a6f13a4aSGreg Roach		$id    = "";
1165a6f13a4aSGreg Roach		$match = array();
1166a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1167a6f13a4aSGreg Roach			$id = $match[1];
1168a6f13a4aSGreg Roach		}
1169a6f13a4aSGreg Roach		$tag = "";
1170a6f13a4aSGreg Roach		if (isset($attrs['ignore'])) {
1171a6f13a4aSGreg Roach			$tag .= $attrs['ignore'];
1172a6f13a4aSGreg Roach		}
1173a6f13a4aSGreg Roach		if (preg_match("/\\$(.+)/", $tag, $match)) {
1174a6f13a4aSGreg Roach			$tag = $vars[$match[1]]['id'];
1175a6f13a4aSGreg Roach		}
1176a6f13a4aSGreg Roach
1177a6f13a4aSGreg Roach		$record = GedcomRecord::getInstance($id, $WT_TREE);
1178a6f13a4aSGreg Roach		if (empty($attrs['diff']) && !empty($id)) {
1179a6f13a4aSGreg Roach			$facts = $record->getFacts();
1180a6f13a4aSGreg Roach			sort_facts($facts);
1181a6f13a4aSGreg Roach			$this->repeats  = array();
1182a6f13a4aSGreg Roach			$nonfacts = explode(',', $tag);
1183a6f13a4aSGreg Roach			foreach ($facts as $event) {
1184a6f13a4aSGreg Roach				if (!in_array($event->getTag(), $nonfacts)) {
1185a6f13a4aSGreg Roach					$this->repeats[] = $event->getGedcom();
1186a6f13a4aSGreg Roach				}
1187a6f13a4aSGreg Roach			}
1188a6f13a4aSGreg Roach		} else {
1189a6f13a4aSGreg Roach			foreach ($record->getFacts() as $fact) {
1190a6f13a4aSGreg Roach				if ($fact->isPendingAddition() && $fact->getTag() !== 'CHAN') {
1191a6f13a4aSGreg Roach					$this->repeats[] = $fact->getGedcom();
1192a6f13a4aSGreg Roach				}
1193a6f13a4aSGreg Roach			}
1194a6f13a4aSGreg Roach		}
1195a6f13a4aSGreg Roach	}
1196a6f13a4aSGreg Roach
1197a6f13a4aSGreg Roach	/**
1198a6f13a4aSGreg Roach	 * XML </ Facts> end element handler
1199a6f13a4aSGreg Roach	 */
1200*8edd1043SGreg Roach	private function factsEndHandler() {
1201a6f13a4aSGreg Roach		global $parser, $parserStack, $report;
1202a6f13a4aSGreg Roach
1203a6f13a4aSGreg Roach		$this->process_repeats--;
1204a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
1205a6f13a4aSGreg Roach			return;
1206a6f13a4aSGreg Roach		}
1207a6f13a4aSGreg Roach
1208a6f13a4aSGreg Roach		// Check if there is anything to repeat
1209a6f13a4aSGreg Roach		if (count($this->repeats) > 0) {
1210a6f13a4aSGreg Roach
1211a6f13a4aSGreg Roach			$line       = xml_get_current_line_number($parser) - 1;
1212a6f13a4aSGreg Roach			$lineoffset = 0;
1213a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
1214a6f13a4aSGreg Roach				$lineoffset += $rep[1];
1215a6f13a4aSGreg Roach			}
1216a6f13a4aSGreg Roach
1217a6f13a4aSGreg Roach			//-- read the xml from the file
1218a6f13a4aSGreg Roach			$lines = file($report);
1219a6f13a4aSGreg Roach			while ($lineoffset + $this->repeat_bytes > 0 && strpos($lines[$lineoffset + $this->repeat_bytes], '<Facts ') === false) {
1220a6f13a4aSGreg Roach				$lineoffset--;
1221a6f13a4aSGreg Roach			}
1222a6f13a4aSGreg Roach			$lineoffset++;
1223a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
1224a6f13a4aSGreg Roach			$i         = $line + $lineoffset;
1225a6f13a4aSGreg Roach			$line_nr   = $this->repeat_bytes + $lineoffset;
1226a6f13a4aSGreg Roach			while ($line_nr < $i) {
1227a6f13a4aSGreg Roach				$reportxml .= $lines[$line_nr];
1228a6f13a4aSGreg Roach				$line_nr++;
1229a6f13a4aSGreg Roach			}
1230a6f13a4aSGreg Roach			// No need to drag this
1231a6f13a4aSGreg Roach			unset($lines);
1232a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>\n";
1233a6f13a4aSGreg Roach			// Save original values
1234a6f13a4aSGreg Roach			array_push($parserStack, $parser);
1235a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
1236a6f13a4aSGreg Roach			$count     = count($this->repeats);
1237a6f13a4aSGreg Roach			$i         = 0;
1238a6f13a4aSGreg Roach			while ($i < $count) {
1239a6f13a4aSGreg Roach				$this->gedrec = $this->repeats[$i];
1240a6f13a4aSGreg Roach				$this->fact   = '';
1241a6f13a4aSGreg Roach				$this->desc   = '';
1242a6f13a4aSGreg Roach				if (preg_match('/1 (\w+)(.*)/', $this->gedrec, $match)) {
1243a6f13a4aSGreg Roach					$this->fact = $match[1];
1244a6f13a4aSGreg Roach					if ($this->fact === 'EVEN' || $this->fact === 'FACT') {
1245a6f13a4aSGreg Roach						$tmatch = array();
1246a6f13a4aSGreg Roach						if (preg_match('/2 TYPE (.+)/', $this->gedrec, $tmatch)) {
1247a6f13a4aSGreg Roach							$this->type = trim($tmatch[1]);
1248a6f13a4aSGreg Roach						} else {
1249a6f13a4aSGreg Roach							$this->type = ' ';
1250a6f13a4aSGreg Roach						}
1251a6f13a4aSGreg Roach					}
1252a6f13a4aSGreg Roach					$this->desc = trim($match[2]);
1253a6f13a4aSGreg Roach					$this->desc .= get_cont(2, $this->gedrec);
1254a6f13a4aSGreg Roach				}
1255a6f13a4aSGreg Roach				$repeat_parser = xml_parser_create();
1256a6f13a4aSGreg Roach				$parser        = $repeat_parser;
1257a6f13a4aSGreg Roach				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1258a6f13a4aSGreg Roach				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1259a6f13a4aSGreg Roach				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1260a6f13a4aSGreg Roach				if (!xml_parse($repeat_parser, $reportxml, true)) {
1261a6f13a4aSGreg Roach					throw new \DomainException(sprintf(
1262a6f13a4aSGreg Roach						'FactsEHandler XML error: %s at line %d',
1263a6f13a4aSGreg Roach						xml_error_string(xml_get_error_code($repeat_parser)),
1264a6f13a4aSGreg Roach						xml_get_current_line_number($repeat_parser)
1265a6f13a4aSGreg Roach					));
1266a6f13a4aSGreg Roach				}
1267a6f13a4aSGreg Roach				xml_parser_free($repeat_parser);
1268a6f13a4aSGreg Roach				$i++;
1269a6f13a4aSGreg Roach			}
1270a6f13a4aSGreg Roach			// Restore original values
1271a6f13a4aSGreg Roach			$parser = array_pop($parserStack);
1272a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
1273a6f13a4aSGreg Roach		}
1274a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1275a6f13a4aSGreg Roach	}
1276a6f13a4aSGreg Roach
1277a6f13a4aSGreg Roach	/**
1278a6f13a4aSGreg Roach	 * Setting upp or changing variables in the XML
1279a6f13a4aSGreg Roach	 * The XML variable name and value is stored in the global variable $vars
1280a6f13a4aSGreg Roach	 *
1281a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1282a6f13a4aSGreg Roach	 */
1283*8edd1043SGreg Roach	private function setVarStartHandler($attrs) {
1284a6f13a4aSGreg Roach		global $vars;
1285a6f13a4aSGreg Roach
1286a6f13a4aSGreg Roach		if (empty($attrs['name'])) {
1287a6f13a4aSGreg Roach			throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
1288a6f13a4aSGreg Roach		}
1289a6f13a4aSGreg Roach
1290a6f13a4aSGreg Roach		$name  = $attrs['name'];
1291a6f13a4aSGreg Roach		$value = $attrs['value'];
1292a6f13a4aSGreg Roach		$match = array();
1293a6f13a4aSGreg Roach		// Current GEDCOM record strings
1294a6f13a4aSGreg Roach		if ($value == "@ID") {
1295a6f13a4aSGreg Roach			if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1296a6f13a4aSGreg Roach				$value = $match[1];
1297a6f13a4aSGreg Roach			}
1298a6f13a4aSGreg Roach		} elseif ($value == "@fact") {
1299a6f13a4aSGreg Roach			$value = $this->fact;
1300a6f13a4aSGreg Roach		} elseif ($value == "@desc") {
1301a6f13a4aSGreg Roach			$value = $this->desc;
1302a6f13a4aSGreg Roach		} elseif ($value == "@generation") {
1303a6f13a4aSGreg Roach			$value = $this->generation;
1304a6f13a4aSGreg Roach		} elseif (preg_match("/@(\w+)/", $value, $match)) {
1305a6f13a4aSGreg Roach			$gmatch = array();
1306a6f13a4aSGreg Roach			if (preg_match("/\d $match[1] (.+)/", $this->gedrec, $gmatch)) {
1307a6f13a4aSGreg Roach				$value = str_replace("@", "", trim($gmatch[1]));
1308a6f13a4aSGreg Roach			}
1309a6f13a4aSGreg Roach		}
1310a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $name, $match)) {
1311a6f13a4aSGreg Roach			$name = $vars["'" . $match[1] . "'"]['id'];
1312a6f13a4aSGreg Roach		}
1313a6f13a4aSGreg Roach		$count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
1314a6f13a4aSGreg Roach		$i     = 0;
1315a6f13a4aSGreg Roach		while ($i < $count) {
1316a6f13a4aSGreg Roach			$t     = $vars[$match[$i][1]]['id'];
1317a6f13a4aSGreg Roach			$value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
1318a6f13a4aSGreg Roach			$i++;
1319a6f13a4aSGreg Roach		}
1320a6f13a4aSGreg Roach		if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
1321a6f13a4aSGreg Roach			$value = I18N::number($match[1]);
1322a6f13a4aSGreg Roach		} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
1323a6f13a4aSGreg Roach			$value = I18N::translate($match[1]);
1324a6f13a4aSGreg Roach		} elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
1325a6f13a4aSGreg Roach			$value = I18N::translateContext($match[1], $match[2]);
1326a6f13a4aSGreg Roach		}
1327a6f13a4aSGreg Roach		// Arithmetic functions
1328a6f13a4aSGreg Roach		if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
1329a6f13a4aSGreg Roach			switch ($match[2]) {
1330a6f13a4aSGreg Roach				case "+":
1331a6f13a4aSGreg Roach					$t     = $match[1] + $match[3];
1332a6f13a4aSGreg Roach					$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1333a6f13a4aSGreg Roach					break;
1334a6f13a4aSGreg Roach				case "-":
1335a6f13a4aSGreg Roach					$t     = $match[1] - $match[3];
1336a6f13a4aSGreg Roach					$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1337a6f13a4aSGreg Roach					break;
1338a6f13a4aSGreg Roach				case "*":
1339a6f13a4aSGreg Roach					$t     = $match[1] * $match[3];
1340a6f13a4aSGreg Roach					$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1341a6f13a4aSGreg Roach					break;
1342a6f13a4aSGreg Roach				case "/":
1343a6f13a4aSGreg Roach					$t     = $match[1] / $match[3];
1344a6f13a4aSGreg Roach					$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1345a6f13a4aSGreg Roach					break;
1346a6f13a4aSGreg Roach			}
1347a6f13a4aSGreg Roach		}
1348a6f13a4aSGreg Roach		if (strpos($value, "@") !== false) {
1349a6f13a4aSGreg Roach			$value = "";
1350a6f13a4aSGreg Roach		}
1351a6f13a4aSGreg Roach		$vars[$name]['id'] = $value;
1352a6f13a4aSGreg Roach	}
1353a6f13a4aSGreg Roach
1354a6f13a4aSGreg Roach	/**
1355a6f13a4aSGreg Roach	 * XML <if > start element
1356a6f13a4aSGreg Roach	 *
1357a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1358a6f13a4aSGreg Roach	 */
1359*8edd1043SGreg Roach	private function ifStartHandler($attrs) {
1360a6f13a4aSGreg Roach		global $vars;
1361a6f13a4aSGreg Roach
1362a6f13a4aSGreg Roach		if ($this->process_ifs > 0) {
1363a6f13a4aSGreg Roach			$this->process_ifs++;
1364a6f13a4aSGreg Roach
1365a6f13a4aSGreg Roach			return;
1366a6f13a4aSGreg Roach		}
1367a6f13a4aSGreg Roach
1368a6f13a4aSGreg Roach		$condition = $attrs['condition'];
1369a6f13a4aSGreg Roach		$condition = preg_replace("/\\$(\w+)/", "\$vars[\"$1\"][\"id\"]", $condition);
1370a6f13a4aSGreg Roach		$condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
1371a6f13a4aSGreg Roach		// Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
1372a6f13a4aSGreg Roach		$condition = str_replace("@fact", $this->fact, $condition);
1373a6f13a4aSGreg Roach		$match     = array();
1374a6f13a4aSGreg Roach		$count     = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
1375a6f13a4aSGreg Roach		$i         = 0;
1376a6f13a4aSGreg Roach		while ($i < $count) {
1377a6f13a4aSGreg Roach			$id    = $match[$i][1];
1378a6f13a4aSGreg Roach			$value = '""';
1379a6f13a4aSGreg Roach			if ($id == "ID") {
1380a6f13a4aSGreg Roach				if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1381a6f13a4aSGreg Roach					$value = "'" . $match[1] . "'";
1382a6f13a4aSGreg Roach				}
1383a6f13a4aSGreg Roach			} elseif ($id === "fact") {
1384a6f13a4aSGreg Roach				$value = '"' . $this->fact . '"';
1385a6f13a4aSGreg Roach			} elseif ($id === "desc") {
1386a6f13a4aSGreg Roach				$value = '"' . addslashes($this->desc) . '"';
1387a6f13a4aSGreg Roach			} elseif ($id === "generation") {
1388a6f13a4aSGreg Roach				$value = '"' . $this->generation . '"';
1389a6f13a4aSGreg Roach			} else {
1390a6f13a4aSGreg Roach
1391a6f13a4aSGreg Roach				$temp  = explode(" ", trim($this->gedrec));
1392a6f13a4aSGreg Roach				$level = $temp[0];
1393a6f13a4aSGreg Roach				if ($level == 0) {
1394a6f13a4aSGreg Roach					$level++;
1395a6f13a4aSGreg Roach				}
1396a6f13a4aSGreg Roach				$value = get_gedcom_value($id, $level, $this->gedrec);
1397a6f13a4aSGreg Roach				if (empty($value)) {
1398a6f13a4aSGreg Roach					$level++;
1399a6f13a4aSGreg Roach					$value = get_gedcom_value($id, $level, $this->gedrec);
1400a6f13a4aSGreg Roach				}
1401a6f13a4aSGreg Roach				$value = preg_replace("/^@(" . WT_REGEX_XREF . ")@$/", "$1", $value);
1402a6f13a4aSGreg Roach				$value = "\"" . addslashes($value) . "\"";
1403a6f13a4aSGreg Roach			}
1404a6f13a4aSGreg Roach			$condition = str_replace("@$id", $value, $condition);
1405a6f13a4aSGreg Roach			$i++;
1406a6f13a4aSGreg Roach		}
1407a6f13a4aSGreg Roach		$condition = "return (bool) ($condition);";
1408a6f13a4aSGreg Roach		$ret       = @eval($condition);
1409a6f13a4aSGreg Roach		if (!$ret) {
1410a6f13a4aSGreg Roach			$this->process_ifs++;
1411a6f13a4aSGreg Roach		}
1412a6f13a4aSGreg Roach	}
1413a6f13a4aSGreg Roach
1414a6f13a4aSGreg Roach	/**
1415a6f13a4aSGreg Roach	 * XML <if /> end element
1416a6f13a4aSGreg Roach	 */
1417*8edd1043SGreg Roach	private function ifEndHandler() {
1418a6f13a4aSGreg Roach		if ($this->process_ifs > 0) {
1419a6f13a4aSGreg Roach			$this->process_ifs--;
1420a6f13a4aSGreg Roach		}
1421a6f13a4aSGreg Roach	}
1422a6f13a4aSGreg Roach
1423a6f13a4aSGreg Roach	/**
1424a6f13a4aSGreg Roach	 * XML <Footnote > start element
1425a6f13a4aSGreg Roach	 * Collect the Footnote links
1426a6f13a4aSGreg Roach	 * GEDCOM Records that are protected by Privacy setting will be ignore
1427a6f13a4aSGreg Roach	 *
1428a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1429a6f13a4aSGreg Roach	 */
1430*8edd1043SGreg Roach	private function footnoteStartHandler($attrs) {
1431a6f13a4aSGreg Roach		global $ReportRoot, $WT_TREE;
1432a6f13a4aSGreg Roach
1433a6f13a4aSGreg Roach		$id = "";
1434a6f13a4aSGreg Roach		if (preg_match("/[0-9] (.+) @(.+)@/", $this->gedrec, $match)) {
1435a6f13a4aSGreg Roach			$id = $match[2];
1436a6f13a4aSGreg Roach		}
1437a6f13a4aSGreg Roach		$record = GedcomRecord::GetInstance($id, $WT_TREE);
1438a6f13a4aSGreg Roach		if ($record && $record->canShow()) {
1439a6f13a4aSGreg Roach			array_push($this->print_data_stack, $this->print_data);
1440a6f13a4aSGreg Roach			$this->print_data = true;
1441a6f13a4aSGreg Roach			$style            = "";
1442a6f13a4aSGreg Roach			if (!empty($attrs['style'])) {
1443a6f13a4aSGreg Roach				$style = $attrs['style'];
1444a6f13a4aSGreg Roach			}
1445a6f13a4aSGreg Roach			$this->footnote_element = $this->current_element;
1446a6f13a4aSGreg Roach			$this->current_element  = $ReportRoot->createFootnote($style);
1447a6f13a4aSGreg Roach		} else {
1448a6f13a4aSGreg Roach			$this->print_data       = false;
1449a6f13a4aSGreg Roach			$this->process_footnote = false;
1450a6f13a4aSGreg Roach		}
1451a6f13a4aSGreg Roach	}
1452a6f13a4aSGreg Roach
1453a6f13a4aSGreg Roach	/**
1454a6f13a4aSGreg Roach	 * XML <Footnote /> end element
1455a6f13a4aSGreg Roach	 * Print the collected Footnote data
1456a6f13a4aSGreg Roach	 */
1457*8edd1043SGreg Roach	private function footnoteEndHandler() {
1458a6f13a4aSGreg Roach		global $wt_report;
1459a6f13a4aSGreg Roach
1460a6f13a4aSGreg Roach		if ($this->process_footnote) {
1461a6f13a4aSGreg Roach			$this->print_data = array_pop($this->print_data_stack);
1462a6f13a4aSGreg Roach			$temp             = trim($this->current_element->getValue());
1463a6f13a4aSGreg Roach			if (strlen($temp) > 3) {
1464a6f13a4aSGreg Roach				$wt_report->addElement($this->current_element);
1465a6f13a4aSGreg Roach			}
1466a6f13a4aSGreg Roach			$this->current_element = $this->footnote_element;
1467a6f13a4aSGreg Roach		} else {
1468a6f13a4aSGreg Roach			$this->process_footnote = true;
1469a6f13a4aSGreg Roach		}
1470a6f13a4aSGreg Roach	}
1471a6f13a4aSGreg Roach
1472a6f13a4aSGreg Roach	/**
1473a6f13a4aSGreg Roach	 * XML <FootnoteTexts /> element
1474a6f13a4aSGreg Roach	 */
1475*8edd1043SGreg Roach	private function footnoteTextsStartHandler() {
1476a6f13a4aSGreg Roach		global $wt_report;
1477a6f13a4aSGreg Roach
1478a6f13a4aSGreg Roach		$temp = "footnotetexts";
1479a6f13a4aSGreg Roach		$wt_report->addElement($temp);
1480a6f13a4aSGreg Roach	}
1481a6f13a4aSGreg Roach
1482a6f13a4aSGreg Roach	/**
1483a6f13a4aSGreg Roach	 * XML <AgeAtDeath /> element handler
1484a6f13a4aSGreg Roach	 */
1485*8edd1043SGreg Roach	private function ageAtDeathStartHandler() {
1486a6f13a4aSGreg Roach		// This duplicates functionality in format_fact_date()
1487a6f13a4aSGreg Roach		global $factrec, $WT_TREE;
1488a6f13a4aSGreg Roach
1489a6f13a4aSGreg Roach		$match = array();
1490a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1491a6f13a4aSGreg Roach			$person = Individual::getInstance($match[1], $WT_TREE);
1492a6f13a4aSGreg Roach			// Recorded age
1493a6f13a4aSGreg Roach			if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
1494a6f13a4aSGreg Roach				$fact_age = $match[1];
1495a6f13a4aSGreg Roach			} else {
1496a6f13a4aSGreg Roach				$fact_age = '';
1497a6f13a4aSGreg Roach			}
1498a6f13a4aSGreg Roach			if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
1499a6f13a4aSGreg Roach				$husb_age = $match[1];
1500a6f13a4aSGreg Roach			} else {
1501a6f13a4aSGreg Roach				$husb_age = '';
1502a6f13a4aSGreg Roach			}
1503a6f13a4aSGreg Roach			if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
1504a6f13a4aSGreg Roach				$wife_age = $match[1];
1505a6f13a4aSGreg Roach			} else {
1506a6f13a4aSGreg Roach				$wife_age = '';
1507a6f13a4aSGreg Roach			}
1508a6f13a4aSGreg Roach
1509a6f13a4aSGreg Roach			// Calculated age
1510a6f13a4aSGreg Roach			$birth_date = $person->getBirthDate();
1511a6f13a4aSGreg Roach			// Can't use getDeathDate(), as this also gives BURI/CREM events, which
1512a6f13a4aSGreg Roach			// wouldn't give the correct "days after death" result for people with
1513a6f13a4aSGreg Roach			// no DEAT.
1514a6f13a4aSGreg Roach			$death_event = $person->getFirstFact('DEAT');
1515a6f13a4aSGreg Roach			if ($death_event) {
1516a6f13a4aSGreg Roach				$death_date = $death_event->getDate();
1517a6f13a4aSGreg Roach			} else {
1518a6f13a4aSGreg Roach				$death_date = new Date('');
1519a6f13a4aSGreg Roach			}
1520a6f13a4aSGreg Roach			$value = '';
1521a6f13a4aSGreg Roach			if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
1522a6f13a4aSGreg Roach				$age = Date::getAgeGedcom($birth_date, $death_date);
1523a6f13a4aSGreg Roach				// Only show calculated age if it differs from recorded age
1524a6f13a4aSGreg Roach				if ($age != '' && $age != "0d") {
1525a6f13a4aSGreg Roach					if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
1526a6f13a4aSGreg Roach					) {
1527a6f13a4aSGreg Roach						$value  = get_age_at_event($age, false);
1528a6f13a4aSGreg Roach						$abbrev = substr($value, 0, strpos($value, ' ') + 5);
1529a6f13a4aSGreg Roach						if ($value !== $abbrev) {
1530a6f13a4aSGreg Roach							$value = $abbrev . '.';
1531a6f13a4aSGreg Roach						}
1532a6f13a4aSGreg Roach					}
1533a6f13a4aSGreg Roach				}
1534a6f13a4aSGreg Roach			}
1535a6f13a4aSGreg Roach			$this->current_element->addText($value);
1536a6f13a4aSGreg Roach		}
1537a6f13a4aSGreg Roach	}
1538a6f13a4aSGreg Roach
1539a6f13a4aSGreg Roach	/**
1540a6f13a4aSGreg Roach	 * XML element Forced line break handler - HTML code
1541a6f13a4aSGreg Roach	 */
1542*8edd1043SGreg Roach	private function brStartHandler() {
1543a6f13a4aSGreg Roach		if ($this->print_data && $this->process_gedcoms === 0) {
1544a6f13a4aSGreg Roach			$this->current_element->addText('<br>');
1545a6f13a4aSGreg Roach		}
1546a6f13a4aSGreg Roach	}
1547a6f13a4aSGreg Roach
1548a6f13a4aSGreg Roach	/**
1549a6f13a4aSGreg Roach	 * XML <sp />element Forced space handler
1550a6f13a4aSGreg Roach	 */
1551*8edd1043SGreg Roach	private function spStartHandler() {
1552a6f13a4aSGreg Roach		if ($this->print_data && $this->process_gedcoms === 0) {
1553a6f13a4aSGreg Roach			$this->current_element->addText(' ');
1554a6f13a4aSGreg Roach		}
1555a6f13a4aSGreg Roach	}
1556a6f13a4aSGreg Roach
1557a6f13a4aSGreg Roach	/**
1558a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1559a6f13a4aSGreg Roach	 */
1560*8edd1043SGreg Roach	private function highlightedImageStartHandler($attrs) {
1561a6f13a4aSGreg Roach		global $wt_report, $ReportRoot, $WT_TREE;
1562a6f13a4aSGreg Roach
1563a6f13a4aSGreg Roach		$id    = '';
1564a6f13a4aSGreg Roach		$match = array();
1565a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1566a6f13a4aSGreg Roach			$id = $match[1];
1567a6f13a4aSGreg Roach		}
1568a6f13a4aSGreg Roach
1569a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
1570a6f13a4aSGreg Roach		$top = '.';
1571a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
1572a6f13a4aSGreg Roach			if ($attrs['top'] === '0') {
1573a6f13a4aSGreg Roach				$top = 0;
1574a6f13a4aSGreg Roach			} elseif ($attrs['top'] === '.') {
1575a6f13a4aSGreg Roach				$top = '.';
1576a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
1577a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
1578a6f13a4aSGreg Roach			}
1579a6f13a4aSGreg Roach		}
1580a6f13a4aSGreg Roach
1581a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. the default is the current position
1582a6f13a4aSGreg Roach		$left = '.';
1583a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
1584a6f13a4aSGreg Roach			if ($attrs['left'] === '0') {
1585a6f13a4aSGreg Roach				$left = 0;
1586a6f13a4aSGreg Roach			} elseif ($attrs['left'] === '.') {
1587a6f13a4aSGreg Roach				$left = '.';
1588a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
1589a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
1590a6f13a4aSGreg Roach			}
1591a6f13a4aSGreg Roach		}
1592a6f13a4aSGreg Roach
1593a6f13a4aSGreg Roach		// string Align the image in left, center, right
1594a6f13a4aSGreg Roach		$align = '';
1595a6f13a4aSGreg Roach		if (!empty($attrs['align'])) {
1596a6f13a4aSGreg Roach			$align = $attrs['align'];
1597a6f13a4aSGreg Roach		}
1598a6f13a4aSGreg Roach
1599a6f13a4aSGreg Roach		// string Next Line should be T:next to the image, N:next line
1600a6f13a4aSGreg Roach		$ln = '';
1601a6f13a4aSGreg Roach		if (!empty($attrs['ln'])) {
1602a6f13a4aSGreg Roach			$ln = $attrs['ln'];
1603a6f13a4aSGreg Roach		}
1604a6f13a4aSGreg Roach
1605a6f13a4aSGreg Roach		$width  = 0;
1606a6f13a4aSGreg Roach		$height = 0;
1607a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
1608a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
1609a6f13a4aSGreg Roach		}
1610a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
1611a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
1612a6f13a4aSGreg Roach		}
1613a6f13a4aSGreg Roach
1614a6f13a4aSGreg Roach		$person      = Individual::getInstance($id, $WT_TREE);
1615a6f13a4aSGreg Roach		$mediaobject = $person->findHighlightedMedia();
1616a6f13a4aSGreg Roach		if ($mediaobject) {
1617a6f13a4aSGreg Roach			$attributes = $mediaobject->getImageAttributes('thumb');
1618a6f13a4aSGreg Roach			if (in_array(
1619a6f13a4aSGreg Roach					$attributes['ext'],
1620a6f13a4aSGreg Roach					array(
1621a6f13a4aSGreg Roach						'GIF',
1622a6f13a4aSGreg Roach						'JPG',
1623a6f13a4aSGreg Roach						'PNG',
1624a6f13a4aSGreg Roach						'SWF',
1625a6f13a4aSGreg Roach						'PSD',
1626a6f13a4aSGreg Roach						'BMP',
1627a6f13a4aSGreg Roach						'TIFF',
1628a6f13a4aSGreg Roach						'TIFF',
1629a6f13a4aSGreg Roach						'JPC',
1630a6f13a4aSGreg Roach						'JP2',
1631a6f13a4aSGreg Roach						'JPX',
1632a6f13a4aSGreg Roach						'JB2',
1633a6f13a4aSGreg Roach						'SWC',
1634a6f13a4aSGreg Roach						'IFF',
1635a6f13a4aSGreg Roach						'WBMP',
1636a6f13a4aSGreg Roach						'XBM',
1637a6f13a4aSGreg Roach					)
1638a6f13a4aSGreg Roach				) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1639a6f13a4aSGreg Roach			) {
1640a6f13a4aSGreg Roach				if ($width > 0 && $height == 0) {
1641a6f13a4aSGreg Roach					$perc   = $width / $attributes['adjW'];
1642a6f13a4aSGreg Roach					$height = round($attributes['adjH'] * $perc);
1643a6f13a4aSGreg Roach				} elseif ($height > 0 && $width == 0) {
1644a6f13a4aSGreg Roach					$perc  = $height / $attributes['adjH'];
1645a6f13a4aSGreg Roach					$width = round($attributes['adjW'] * $perc);
1646a6f13a4aSGreg Roach				} else {
1647a6f13a4aSGreg Roach					$width  = $attributes['adjW'];
1648a6f13a4aSGreg Roach					$height = $attributes['adjH'];
1649a6f13a4aSGreg Roach				}
1650a6f13a4aSGreg Roach				$image = $ReportRoot->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1651a6f13a4aSGreg Roach				$wt_report->addElement($image);
1652a6f13a4aSGreg Roach			}
1653a6f13a4aSGreg Roach		}
1654a6f13a4aSGreg Roach	}
1655a6f13a4aSGreg Roach
1656a6f13a4aSGreg Roach	/**
1657a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1658a6f13a4aSGreg Roach	 */
1659*8edd1043SGreg Roach	private function imageStartHandler($attrs) {
1660a6f13a4aSGreg Roach		global $wt_report, $ReportRoot, $WT_TREE;
1661a6f13a4aSGreg Roach
1662a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
1663a6f13a4aSGreg Roach		$top = '.';
1664a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
1665a6f13a4aSGreg Roach			if ($attrs['top'] === "0") {
1666a6f13a4aSGreg Roach				$top = 0;
1667a6f13a4aSGreg Roach			} elseif ($attrs['top'] === '.') {
1668a6f13a4aSGreg Roach				$top = '.';
1669a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
1670a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
1671a6f13a4aSGreg Roach			}
1672a6f13a4aSGreg Roach		}
1673a6f13a4aSGreg Roach
1674a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. the default is the current position
1675a6f13a4aSGreg Roach		$left = '.';
1676a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
1677a6f13a4aSGreg Roach			if ($attrs['left'] === '0') {
1678a6f13a4aSGreg Roach				$left = 0;
1679a6f13a4aSGreg Roach			} elseif ($attrs['left'] === '.') {
1680a6f13a4aSGreg Roach				$left = '.';
1681a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
1682a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
1683a6f13a4aSGreg Roach			}
1684a6f13a4aSGreg Roach		}
1685a6f13a4aSGreg Roach
1686a6f13a4aSGreg Roach		// string Align the image in left, center, right
1687a6f13a4aSGreg Roach		$align = '';
1688a6f13a4aSGreg Roach		if (!empty($attrs['align'])) {
1689a6f13a4aSGreg Roach			$align = $attrs['align'];
1690a6f13a4aSGreg Roach		}
1691a6f13a4aSGreg Roach
1692a6f13a4aSGreg Roach		// string Next Line should be T:next to the image, N:next line
1693a6f13a4aSGreg Roach		$ln = 'T';
1694a6f13a4aSGreg Roach		if (!empty($attrs['ln'])) {
1695a6f13a4aSGreg Roach			$ln = $attrs['ln'];
1696a6f13a4aSGreg Roach		}
1697a6f13a4aSGreg Roach
1698a6f13a4aSGreg Roach		$width  = 0;
1699a6f13a4aSGreg Roach		$height = 0;
1700a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
1701a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
1702a6f13a4aSGreg Roach		}
1703a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
1704a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
1705a6f13a4aSGreg Roach		}
1706a6f13a4aSGreg Roach
1707a6f13a4aSGreg Roach		$file = '';
1708a6f13a4aSGreg Roach		if (!empty($attrs['file'])) {
1709a6f13a4aSGreg Roach			$file = $attrs['file'];
1710a6f13a4aSGreg Roach		}
1711a6f13a4aSGreg Roach		if ($file == "@FILE") {
1712a6f13a4aSGreg Roach			$match = array();
1713a6f13a4aSGreg Roach			if (preg_match("/\d OBJE @(.+)@/", $this->gedrec, $match)) {
1714a6f13a4aSGreg Roach				$mediaobject = Media::getInstance($match[1], $WT_TREE);
1715a6f13a4aSGreg Roach				$attributes  = $mediaobject->getImageAttributes('thumb');
1716a6f13a4aSGreg Roach				if (in_array(
1717a6f13a4aSGreg Roach						$attributes['ext'],
1718a6f13a4aSGreg Roach						array(
1719a6f13a4aSGreg Roach							'GIF',
1720a6f13a4aSGreg Roach							'JPG',
1721a6f13a4aSGreg Roach							'PNG',
1722a6f13a4aSGreg Roach							'SWF',
1723a6f13a4aSGreg Roach							'PSD',
1724a6f13a4aSGreg Roach							'BMP',
1725a6f13a4aSGreg Roach							'TIFF',
1726a6f13a4aSGreg Roach							'TIFF',
1727a6f13a4aSGreg Roach							'JPC',
1728a6f13a4aSGreg Roach							'JP2',
1729a6f13a4aSGreg Roach							'JPX',
1730a6f13a4aSGreg Roach							'JB2',
1731a6f13a4aSGreg Roach							'SWC',
1732a6f13a4aSGreg Roach							'IFF',
1733a6f13a4aSGreg Roach							'WBMP',
1734a6f13a4aSGreg Roach							'XBM',
1735a6f13a4aSGreg Roach						)
1736a6f13a4aSGreg Roach					) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1737a6f13a4aSGreg Roach				) {
1738a6f13a4aSGreg Roach					if ($width > 0 && $height == 0) {
1739a6f13a4aSGreg Roach						$perc   = $width / $attributes['adjW'];
1740a6f13a4aSGreg Roach						$height = round($attributes['adjH'] * $perc);
1741a6f13a4aSGreg Roach					} elseif ($height > 0 && $width == 0) {
1742a6f13a4aSGreg Roach						$perc  = $height / $attributes['adjH'];
1743a6f13a4aSGreg Roach						$width = round($attributes['adjW'] * $perc);
1744a6f13a4aSGreg Roach					} else {
1745a6f13a4aSGreg Roach						$width  = $attributes['adjW'];
1746a6f13a4aSGreg Roach						$height = $attributes['adjH'];
1747a6f13a4aSGreg Roach					}
1748a6f13a4aSGreg Roach					$image = $ReportRoot->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1749a6f13a4aSGreg Roach					$wt_report->addElement($image);
1750a6f13a4aSGreg Roach				}
1751a6f13a4aSGreg Roach			}
1752a6f13a4aSGreg Roach		} else {
1753a6f13a4aSGreg Roach			if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
1754a6f13a4aSGreg Roach				$size = getimagesize($file);
1755a6f13a4aSGreg Roach				if ($width > 0 && $height == 0) {
1756a6f13a4aSGreg Roach					$perc   = $width / $size[0];
1757a6f13a4aSGreg Roach					$height = round($size[1] * $perc);
1758a6f13a4aSGreg Roach				} elseif ($height > 0 && $width == 0) {
1759a6f13a4aSGreg Roach					$perc  = $height / $size[1];
1760a6f13a4aSGreg Roach					$width = round($size[0] * $perc);
1761a6f13a4aSGreg Roach				} else {
1762a6f13a4aSGreg Roach					$width  = $size[0];
1763a6f13a4aSGreg Roach					$height = $size[1];
1764a6f13a4aSGreg Roach				}
1765a6f13a4aSGreg Roach				$image = $ReportRoot->createImage($file, $left, $top, $width, $height, $align, $ln);
1766a6f13a4aSGreg Roach				$wt_report->addElement($image);
1767a6f13a4aSGreg Roach			}
1768a6f13a4aSGreg Roach		}
1769a6f13a4aSGreg Roach	}
1770a6f13a4aSGreg Roach
1771a6f13a4aSGreg Roach	/**
1772a6f13a4aSGreg Roach	 * XML <Line> element handler
1773a6f13a4aSGreg Roach	 *
1774a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1775a6f13a4aSGreg Roach	 */
1776*8edd1043SGreg Roach	private function lineStartHandler($attrs) {
1777a6f13a4aSGreg Roach		global $wt_report, $ReportRoot;
1778a6f13a4aSGreg Roach
1779a6f13a4aSGreg Roach		// Start horizontal position, current position (default)
1780a6f13a4aSGreg Roach		$x1 = ".";
1781a6f13a4aSGreg Roach		if (isset($attrs['x1'])) {
1782a6f13a4aSGreg Roach			if ($attrs['x1'] === "0") {
1783a6f13a4aSGreg Roach				$x1 = 0;
1784a6f13a4aSGreg Roach			} elseif ($attrs['x1'] === ".") {
1785a6f13a4aSGreg Roach				$x1 = ".";
1786a6f13a4aSGreg Roach			} elseif (!empty($attrs['x1'])) {
1787a6f13a4aSGreg Roach				$x1 = (int) $attrs['x1'];
1788a6f13a4aSGreg Roach			}
1789a6f13a4aSGreg Roach		}
1790a6f13a4aSGreg Roach		// Start vertical position, current position (default)
1791a6f13a4aSGreg Roach		$y1 = ".";
1792a6f13a4aSGreg Roach		if (isset($attrs['y1'])) {
1793a6f13a4aSGreg Roach			if ($attrs['y1'] === "0") {
1794a6f13a4aSGreg Roach				$y1 = 0;
1795a6f13a4aSGreg Roach			} elseif ($attrs['y1'] === ".") {
1796a6f13a4aSGreg Roach				$y1 = ".";
1797a6f13a4aSGreg Roach			} elseif (!empty($attrs['y1'])) {
1798a6f13a4aSGreg Roach				$y1 = (int) $attrs['y1'];
1799a6f13a4aSGreg Roach			}
1800a6f13a4aSGreg Roach		}
1801a6f13a4aSGreg Roach		// End horizontal position, maximum width (default)
1802a6f13a4aSGreg Roach		$x2 = ".";
1803a6f13a4aSGreg Roach		if (isset($attrs['x2'])) {
1804a6f13a4aSGreg Roach			if ($attrs['x2'] === "0") {
1805a6f13a4aSGreg Roach				$x2 = 0;
1806a6f13a4aSGreg Roach			} elseif ($attrs['x2'] === ".") {
1807a6f13a4aSGreg Roach				$x2 = ".";
1808a6f13a4aSGreg Roach			} elseif (!empty($attrs['x2'])) {
1809a6f13a4aSGreg Roach				$x2 = (int) $attrs['x2'];
1810a6f13a4aSGreg Roach			}
1811a6f13a4aSGreg Roach		}
1812a6f13a4aSGreg Roach		// End vertical position
1813a6f13a4aSGreg Roach		$y2 = ".";
1814a6f13a4aSGreg Roach		if (isset($attrs['y2'])) {
1815a6f13a4aSGreg Roach			if ($attrs['y2'] === "0") {
1816a6f13a4aSGreg Roach				$y2 = 0;
1817a6f13a4aSGreg Roach			} elseif ($attrs['y2'] === ".") {
1818a6f13a4aSGreg Roach				$y2 = ".";
1819a6f13a4aSGreg Roach			} elseif (!empty($attrs['y2'])) {
1820a6f13a4aSGreg Roach				$y2 = (int) $attrs['y2'];
1821a6f13a4aSGreg Roach			}
1822a6f13a4aSGreg Roach		}
1823a6f13a4aSGreg Roach
1824a6f13a4aSGreg Roach		$line = $ReportRoot->createLine($x1, $y1, $x2, $y2);
1825a6f13a4aSGreg Roach		$wt_report->addElement($line);
1826a6f13a4aSGreg Roach	}
1827a6f13a4aSGreg Roach
1828a6f13a4aSGreg Roach	/**
1829a6f13a4aSGreg Roach	 * XML <List> start element handler
1830a6f13a4aSGreg Roach	 *
1831a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1832a6f13a4aSGreg Roach	 */
1833*8edd1043SGreg Roach	private function listStartHandler($attrs) {
1834a6f13a4aSGreg Roach		global $parser, $vars, $WT_TREE;
1835a6f13a4aSGreg Roach
1836a6f13a4aSGreg Roach		$this->process_repeats++;
1837a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
1838a6f13a4aSGreg Roach			return;
1839a6f13a4aSGreg Roach		}
1840a6f13a4aSGreg Roach
1841a6f13a4aSGreg Roach		$match = array();
1842a6f13a4aSGreg Roach		if (isset($attrs['sortby'])) {
1843a6f13a4aSGreg Roach			$sortby = $attrs['sortby'];
1844a6f13a4aSGreg Roach			if (preg_match("/\\$(\w+)/", $sortby, $match)) {
1845a6f13a4aSGreg Roach				$sortby = $vars[$match[1]]['id'];
1846a6f13a4aSGreg Roach				$sortby = trim($sortby);
1847a6f13a4aSGreg Roach			}
1848a6f13a4aSGreg Roach		} else {
1849a6f13a4aSGreg Roach			$sortby = "NAME";
1850a6f13a4aSGreg Roach		}
1851a6f13a4aSGreg Roach
1852a6f13a4aSGreg Roach		if (isset($attrs['list'])) {
1853a6f13a4aSGreg Roach			$listname = $attrs['list'];
1854a6f13a4aSGreg Roach		} else {
1855a6f13a4aSGreg Roach			$listname = "individual";
1856a6f13a4aSGreg Roach		}
1857a6f13a4aSGreg Roach		// Some filters/sorts can be applied using SQL, while others require PHP
1858a6f13a4aSGreg Roach		switch ($listname) {
1859a6f13a4aSGreg Roach			case "pending":
1860a6f13a4aSGreg Roach				$rows = Database::prepare(
1861a6f13a4aSGreg Roach					"SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" . " FROM `##change`" . " WHERE (xref, change_id) IN (" . "  SELECT xref, MAX(change_id)" . "   FROM `##change`" . "   WHERE status='pending' AND gedcom_id=?" . "   GROUP BY xref" . " )"
1862a6f13a4aSGreg Roach				)->execute(array($WT_TREE->getTreeId()))->fetchAll();
1863a6f13a4aSGreg Roach				$this->list = array();
1864a6f13a4aSGreg Roach				foreach ($rows as $row) {
1865a6f13a4aSGreg Roach					$this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1866a6f13a4aSGreg Roach				}
1867a6f13a4aSGreg Roach				break;
1868a6f13a4aSGreg Roach			case 'individual':
1869a6f13a4aSGreg Roach				$sql_select   = "SELECT DISTINCT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1870a6f13a4aSGreg Roach				$sql_join     = "";
1871a6f13a4aSGreg Roach				$sql_where    = " WHERE i_file = " . $WT_TREE->getTreeId();
1872a6f13a4aSGreg Roach				$sql_order_by = "";
1873a6f13a4aSGreg Roach				foreach ($attrs as $attr => $value) {
1874a6f13a4aSGreg Roach					if (strpos($attr, 'filter') === 0 && $value) {
1875a6f13a4aSGreg Roach						// Substitute global vars
1876a6f13a4aSGreg Roach						$value = preg_replace_callback(
1877a6f13a4aSGreg Roach							'/\$(\w+)/',
1878a6f13a4aSGreg Roach							function ($matches) use ($vars) {
1879a6f13a4aSGreg Roach								return $vars[$matches[1]]['id'];
1880a6f13a4aSGreg Roach							},
1881a6f13a4aSGreg Roach							$value
1882a6f13a4aSGreg Roach						);
1883a6f13a4aSGreg Roach						// Convert the various filters into SQL
1884a6f13a4aSGreg Roach						if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1885a6f13a4aSGreg Roach							$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1886a6f13a4aSGreg Roach							$sql_where .= " AND {$attr}.d_fact='{$match[1]}'";
1887a6f13a4aSGreg Roach							$date = new Date($match[3]);
1888a6f13a4aSGreg Roach							if ($match[2] == "LTE") {
1889a6f13a4aSGreg Roach								$sql_where .= " AND {$attr}.d_julianday2<=" . $date->minimumJulianDay();
1890a6f13a4aSGreg Roach							} else {
1891a6f13a4aSGreg Roach								$sql_where .= " AND {$attr}.d_julianday1>=" . $date->minimumJulianDay();
1892a6f13a4aSGreg Roach							}
1893a6f13a4aSGreg Roach							if ($sortby == $match[1]) {
1894a6f13a4aSGreg Roach								$sortby = "";
1895a6f13a4aSGreg Roach								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1896a6f13a4aSGreg Roach							}
1897a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1898a6f13a4aSGreg Roach						} elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1899a6f13a4aSGreg Roach							// Do nothing, unless you have to
1900a6f13a4aSGreg Roach							if ($match[1] != '' || $sortby == 'NAME') {
1901a6f13a4aSGreg Roach								$sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1902a6f13a4aSGreg Roach								// Search the DB only if there is any name supplied
1903a6f13a4aSGreg Roach								if ($match[1] != "") {
1904a6f13a4aSGreg Roach									$names = explode(" ", $match[1]);
1905a6f13a4aSGreg Roach									foreach ($names as $name) {
1906a6f13a4aSGreg Roach										$sql_where .= " AND {$attr}.n_full LIKE " . Database::quote("%{$name}%");
1907a6f13a4aSGreg Roach									}
1908a6f13a4aSGreg Roach								}
1909a6f13a4aSGreg Roach								// Let the DB do the name sorting even when no name was entered
1910a6f13a4aSGreg Roach								if ($sortby == "NAME") {
1911a6f13a4aSGreg Roach									$sortby = "";
1912a6f13a4aSGreg Roach									$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1913a6f13a4aSGreg Roach								}
1914a6f13a4aSGreg Roach							}
1915a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1916a6f13a4aSGreg Roach						} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1917a6f13a4aSGreg Roach							$sql_where .= " AND i_gedcom REGEXP '" . $match[1] . "'";
1918a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1919a6f13a4aSGreg Roach						} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1920a6f13a4aSGreg Roach							$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=i_file)";
1921a6f13a4aSGreg Roach							$sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=i_id)";
1922a6f13a4aSGreg Roach							$sql_where .= " AND {$attr}a.p_place LIKE " . Database::quote("%{$match[1]}%");
1923a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
1924a6f13a4aSGreg Roach						} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
1925a6f13a4aSGreg Roach							$query = "";
1926a6f13a4aSGreg Roach							// Level 1 tag
1927a6f13a4aSGreg Roach							if ($match[1] !== '') {
1928a6f13a4aSGreg Roach								$query .= "%1 {$match[1]}%";
1929a6f13a4aSGreg Roach							}
1930a6f13a4aSGreg Roach							// Level 2 tag
1931a6f13a4aSGreg Roach							if ($match[2] !== '') {
1932a6f13a4aSGreg Roach								$query .= "%2 {$match[2]}%";
1933a6f13a4aSGreg Roach							}
1934a6f13a4aSGreg Roach							// Contains what?
1935a6f13a4aSGreg Roach							if ($match[3] !== '') {
1936a6f13a4aSGreg Roach								$query .= "%{$match[3]}%";
1937a6f13a4aSGreg Roach							}
1938a6f13a4aSGreg Roach							$sql_where .= " AND i_gedcom LIKE " . Database::quote($query);
1939a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
1940a6f13a4aSGreg Roach						}
1941a6f13a4aSGreg Roach					}
1942a6f13a4aSGreg Roach				}
1943a6f13a4aSGreg Roach
1944a6f13a4aSGreg Roach				$this->list = array();
1945a6f13a4aSGreg Roach				$rows = Database::prepare(
1946a6f13a4aSGreg Roach					$sql_select . $sql_join . $sql_where . $sql_order_by
1947a6f13a4aSGreg Roach				)->fetchAll();
1948a6f13a4aSGreg Roach
1949a6f13a4aSGreg Roach				foreach ($rows as $row) {
1950a6f13a4aSGreg Roach					$this->list[] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1951a6f13a4aSGreg Roach				}
1952a6f13a4aSGreg Roach				break;
1953a6f13a4aSGreg Roach
1954a6f13a4aSGreg Roach			case 'family':
1955a6f13a4aSGreg Roach				$sql_select   = "SELECT DISTINCT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
1956a6f13a4aSGreg Roach				$sql_join     = "";
1957a6f13a4aSGreg Roach				$sql_where    = " WHERE f_file=" . $WT_TREE->getTreeId();
1958a6f13a4aSGreg Roach				$sql_order_by = "";
1959a6f13a4aSGreg Roach				foreach ($attrs as $attr => $value) {
1960a6f13a4aSGreg Roach					if (strpos($attr, 'filter') === 0 && $value) {
1961a6f13a4aSGreg Roach						// Substitute global vars
1962a6f13a4aSGreg Roach						$value = preg_replace_callback(
1963a6f13a4aSGreg Roach							'/\$(\w+)/',
1964a6f13a4aSGreg Roach							function ($matches) use ($vars) {
1965a6f13a4aSGreg Roach								return $vars[$matches[1]]['id'];
1966a6f13a4aSGreg Roach							},
1967a6f13a4aSGreg Roach							$value
1968a6f13a4aSGreg Roach						);
1969a6f13a4aSGreg Roach						// Convert the various filters into SQL
1970a6f13a4aSGreg Roach						if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1971a6f13a4aSGreg Roach							$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
1972a6f13a4aSGreg Roach							$sql_where .= " AND {$attr}.d_fact='{$match[1]}'";
1973a6f13a4aSGreg Roach							$date = new Date($match[3]);
1974a6f13a4aSGreg Roach							if ($match[2] == "LTE") {
1975a6f13a4aSGreg Roach								$sql_where .= " AND {$attr}.d_julianday2<=" . $date->minimumJulianDay();
1976a6f13a4aSGreg Roach							} else {
1977a6f13a4aSGreg Roach								$sql_where .= " AND {$attr}.d_julianday1>=" . $date->minimumJulianDay();
1978a6f13a4aSGreg Roach							}
1979a6f13a4aSGreg Roach							if ($sortby == $match[1]) {
1980a6f13a4aSGreg Roach								$sortby = "";
1981a6f13a4aSGreg Roach								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1982a6f13a4aSGreg Roach							}
1983a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1984a6f13a4aSGreg Roach						} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1985a6f13a4aSGreg Roach							$sql_where .= " AND f_gedcom REGEXP '" . $match[1] . "'";
1986a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1987a6f13a4aSGreg Roach						} elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
1988a6f13a4aSGreg Roach							$sql_join .= " JOIN `##link` AS {$attr}a ON ({$attr}a.l_file=f_file AND {$attr}a.l_from=f_id)";
1989a6f13a4aSGreg Roach							$sql_join .= " JOIN `##name` AS {$attr}b ON ({$attr}b.n_file=f_file AND n_id=f_id)";
1990a6f13a4aSGreg Roach							$sql_where .= " AND {$attr}a.l_type=IN ('HUSB, 'WIFE')";
1991a6f13a4aSGreg Roach							$sql_where .= " AND {$attr}.n_full LIKE " . Database::quote("%{$match[1]}%");
1992a6f13a4aSGreg Roach							if ($sortby == "NAME") {
1993a6f13a4aSGreg Roach								$sortby = "";
1994a6f13a4aSGreg Roach								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1995a6f13a4aSGreg Roach							}
1996a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1997a6f13a4aSGreg Roach						} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1998a6f13a4aSGreg Roach							$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
1999a6f13a4aSGreg Roach							$sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
2000a6f13a4aSGreg Roach							$sql_where .= " AND {$attr}a.p_place LIKE " . Database::quote("%{$match[1]}%");
2001a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
2002a6f13a4aSGreg Roach						} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
2003a6f13a4aSGreg Roach							$query = "";
2004a6f13a4aSGreg Roach							// Level 1 tag
2005a6f13a4aSGreg Roach							if ($match[1] !== '') {
2006a6f13a4aSGreg Roach								$query .= "%1 {$match[1]}%";
2007a6f13a4aSGreg Roach							}
2008a6f13a4aSGreg Roach							// Level 2 tag
2009a6f13a4aSGreg Roach							if ($match[2] !== '') {
2010a6f13a4aSGreg Roach								$query .= "%2 {$match[2]}%";
2011a6f13a4aSGreg Roach							}
2012a6f13a4aSGreg Roach							// Contains what?
2013a6f13a4aSGreg Roach							if ($match[3] !== '') {
2014a6f13a4aSGreg Roach								$query .= "%{$match[3]}%";
2015a6f13a4aSGreg Roach							}
2016a6f13a4aSGreg Roach							$sql_where .= " AND f_gedcom LIKE " . Database::quote($query);
2017a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
2018a6f13a4aSGreg Roach						}
2019a6f13a4aSGreg Roach					}
2020a6f13a4aSGreg Roach				}
2021a6f13a4aSGreg Roach
2022a6f13a4aSGreg Roach				$this->list = array();
2023a6f13a4aSGreg Roach				$rows = Database::prepare(
2024a6f13a4aSGreg Roach					$sql_select . $sql_join . $sql_where . $sql_order_by
2025a6f13a4aSGreg Roach				)->fetchAll();
2026a6f13a4aSGreg Roach
2027a6f13a4aSGreg Roach				foreach ($rows as $row) {
2028a6f13a4aSGreg Roach					$this->list[] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2029a6f13a4aSGreg Roach				}
2030a6f13a4aSGreg Roach				break;
2031a6f13a4aSGreg Roach
2032a6f13a4aSGreg Roach			default:
2033a6f13a4aSGreg Roach				throw new \DomainException('Invalid list name: ' . $listname);
2034a6f13a4aSGreg Roach		}
2035a6f13a4aSGreg Roach
2036a6f13a4aSGreg Roach		$filters  = array();
2037a6f13a4aSGreg Roach		$filters2 = array();
2038a6f13a4aSGreg Roach		if (isset($attrs['filter1']) && count($this->list) > 0) {
2039a6f13a4aSGreg Roach			foreach ($attrs as $key => $value) {
2040a6f13a4aSGreg Roach				if (preg_match("/filter(\d)/", $key)) {
2041a6f13a4aSGreg Roach					$condition = $value;
2042a6f13a4aSGreg Roach					if (preg_match("/@(\w+)/", $condition, $match)) {
2043a6f13a4aSGreg Roach						$id    = $match[1];
2044a6f13a4aSGreg Roach						$value = "''";
2045a6f13a4aSGreg Roach						if ($id == "ID") {
2046a6f13a4aSGreg Roach							if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
2047a6f13a4aSGreg Roach								$value = "'" . $match[1] . "'";
2048a6f13a4aSGreg Roach							}
2049a6f13a4aSGreg Roach						} elseif ($id == "fact") {
2050a6f13a4aSGreg Roach							$value = "'" . $this->fact . "'";
2051a6f13a4aSGreg Roach						} elseif ($id == "desc") {
2052a6f13a4aSGreg Roach							$value = "'" . $this->desc . "'";
2053a6f13a4aSGreg Roach						} else {
2054a6f13a4aSGreg Roach							if (preg_match("/\d $id (.+)/", $this->gedrec, $match)) {
2055a6f13a4aSGreg Roach								$value = "'" . str_replace("@", "", trim($match[1])) . "'";
2056a6f13a4aSGreg Roach							}
2057a6f13a4aSGreg Roach						}
2058a6f13a4aSGreg Roach						$condition = preg_replace("/@$id/", $value, $condition);
2059a6f13a4aSGreg Roach					}
2060a6f13a4aSGreg Roach					//-- handle regular expressions
2061a6f13a4aSGreg Roach					if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
2062a6f13a4aSGreg Roach						$tag  = trim($match[1]);
2063a6f13a4aSGreg Roach						$expr = trim($match[2]);
2064a6f13a4aSGreg Roach						$val  = trim($match[3]);
2065a6f13a4aSGreg Roach						if (preg_match("/\\$(\w+)/", $val, $match)) {
2066a6f13a4aSGreg Roach							$val = $vars[$match[1]]['id'];
2067a6f13a4aSGreg Roach							$val = trim($val);
2068a6f13a4aSGreg Roach						}
2069a6f13a4aSGreg Roach						if ($val) {
2070a6f13a4aSGreg Roach							$searchstr = "";
2071a6f13a4aSGreg Roach							$tags      = explode(":", $tag);
2072a6f13a4aSGreg Roach							//-- only limit to a level number if we are specifically looking at a level
2073a6f13a4aSGreg Roach							if (count($tags) > 1) {
2074a6f13a4aSGreg Roach								$level = 1;
2075a6f13a4aSGreg Roach								foreach ($tags as $t) {
2076a6f13a4aSGreg Roach									if (!empty($searchstr)) {
2077a6f13a4aSGreg Roach										$searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
2078a6f13a4aSGreg Roach									}
2079a6f13a4aSGreg Roach									//-- search for both EMAIL and _EMAIL... silly double gedcom standard
2080a6f13a4aSGreg Roach									if ($t == "EMAIL" || $t == "_EMAIL") {
2081a6f13a4aSGreg Roach										$t = "_?EMAIL";
2082a6f13a4aSGreg Roach									}
2083a6f13a4aSGreg Roach									$searchstr .= $level . " " . $t;
2084a6f13a4aSGreg Roach									$level++;
2085a6f13a4aSGreg Roach								}
2086a6f13a4aSGreg Roach							} else {
2087a6f13a4aSGreg Roach								if ($tag == "EMAIL" || $tag == "_EMAIL") {
2088a6f13a4aSGreg Roach									$tag = "_?EMAIL";
2089a6f13a4aSGreg Roach								}
2090a6f13a4aSGreg Roach								$t         = $tag;
2091a6f13a4aSGreg Roach								$searchstr = "1 " . $tag;
2092a6f13a4aSGreg Roach							}
2093a6f13a4aSGreg Roach							switch ($expr) {
2094a6f13a4aSGreg Roach								case "CONTAINS":
2095a6f13a4aSGreg Roach									if ($t == "PLAC") {
2096a6f13a4aSGreg Roach										$searchstr .= "[^\n]*[, ]*" . $val;
2097a6f13a4aSGreg Roach									} else {
2098a6f13a4aSGreg Roach										$searchstr .= "[^\n]*" . $val;
2099a6f13a4aSGreg Roach									}
2100a6f13a4aSGreg Roach									$filters[] = $searchstr;
2101a6f13a4aSGreg Roach									break;
2102a6f13a4aSGreg Roach								default:
2103a6f13a4aSGreg Roach									$filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2104a6f13a4aSGreg Roach									break;
2105a6f13a4aSGreg Roach							}
2106a6f13a4aSGreg Roach						}
2107a6f13a4aSGreg Roach					}
2108a6f13a4aSGreg Roach				}
2109a6f13a4aSGreg Roach			}
2110a6f13a4aSGreg Roach		}
2111a6f13a4aSGreg Roach		//-- apply other filters to the list that could not be added to the search string
2112a6f13a4aSGreg Roach		if ($filters) {
2113a6f13a4aSGreg Roach			foreach ($this->list as $key => $record) {
2114a6f13a4aSGreg Roach				foreach ($filters as $filter) {
2115a6f13a4aSGreg Roach					if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
2116a6f13a4aSGreg Roach						unset($this->list[$key]);
2117a6f13a4aSGreg Roach						break;
2118a6f13a4aSGreg Roach					}
2119a6f13a4aSGreg Roach				}
2120a6f13a4aSGreg Roach			}
2121a6f13a4aSGreg Roach		}
2122a6f13a4aSGreg Roach		if ($filters2) {
2123a6f13a4aSGreg Roach			$mylist = array();
2124a6f13a4aSGreg Roach			foreach ($this->list as $indi) {
2125a6f13a4aSGreg Roach				$key  = $indi->getXref();
2126a6f13a4aSGreg Roach				$grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
2127a6f13a4aSGreg Roach				$keep = true;
2128a6f13a4aSGreg Roach				foreach ($filters2 as $filter) {
2129a6f13a4aSGreg Roach					if ($keep) {
2130a6f13a4aSGreg Roach						$tag  = $filter['tag'];
2131a6f13a4aSGreg Roach						$expr = $filter['expr'];
2132a6f13a4aSGreg Roach						$val  = $filter['val'];
2133a6f13a4aSGreg Roach						if ($val == "''") {
2134a6f13a4aSGreg Roach							$val = "";
2135a6f13a4aSGreg Roach						}
2136a6f13a4aSGreg Roach						$tags = explode(":", $tag);
2137a6f13a4aSGreg Roach						$t    = end($tags);
2138a6f13a4aSGreg Roach						$v    = get_gedcom_value($tag, 1, $grec);
2139a6f13a4aSGreg Roach						//-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
2140a6f13a4aSGreg Roach						if ($t == "EMAIL" && empty($v)) {
2141a6f13a4aSGreg Roach							$tag  = str_replace("EMAIL", "_EMAIL", $tag);
2142a6f13a4aSGreg Roach							$tags = explode(":", $tag);
2143a6f13a4aSGreg Roach							$t    = end($tags);
2144a6f13a4aSGreg Roach							$v    = get_sub_record(1, $tag, $grec);
2145a6f13a4aSGreg Roach						}
2146a6f13a4aSGreg Roach
2147a6f13a4aSGreg Roach						switch ($expr) {
2148a6f13a4aSGreg Roach							case "GTE":
2149a6f13a4aSGreg Roach								if ($t == "DATE") {
2150a6f13a4aSGreg Roach									$date1 = new Date($v);
2151a6f13a4aSGreg Roach									$date2 = new Date($val);
2152a6f13a4aSGreg Roach									$keep  = (Date::compare($date1, $date2) >= 0);
2153a6f13a4aSGreg Roach								} elseif ($val >= $v) {
2154a6f13a4aSGreg Roach									$keep = true;
2155a6f13a4aSGreg Roach								}
2156a6f13a4aSGreg Roach								break;
2157a6f13a4aSGreg Roach							case "LTE":
2158a6f13a4aSGreg Roach								if ($t == "DATE") {
2159a6f13a4aSGreg Roach									$date1 = new Date($v);
2160a6f13a4aSGreg Roach									$date2 = new Date($val);
2161a6f13a4aSGreg Roach									$keep  = (Date::compare($date1, $date2) <= 0);
2162a6f13a4aSGreg Roach								} elseif ($val >= $v) {
2163a6f13a4aSGreg Roach									$keep = true;
2164a6f13a4aSGreg Roach								}
2165a6f13a4aSGreg Roach								break;
2166a6f13a4aSGreg Roach							default:
2167a6f13a4aSGreg Roach								if ($v == $val) {
2168a6f13a4aSGreg Roach									$keep = true;
2169a6f13a4aSGreg Roach								} else {
2170a6f13a4aSGreg Roach									$keep = false;
2171a6f13a4aSGreg Roach								}
2172a6f13a4aSGreg Roach								break;
2173a6f13a4aSGreg Roach						}
2174a6f13a4aSGreg Roach					}
2175a6f13a4aSGreg Roach				}
2176a6f13a4aSGreg Roach				if ($keep) {
2177a6f13a4aSGreg Roach					$mylist[$key] = $indi;
2178a6f13a4aSGreg Roach				}
2179a6f13a4aSGreg Roach			}
2180a6f13a4aSGreg Roach			$this->list = $mylist;
2181a6f13a4aSGreg Roach		}
2182a6f13a4aSGreg Roach
2183a6f13a4aSGreg Roach		switch ($sortby) {
2184a6f13a4aSGreg Roach			case 'NAME':
2185a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2186a6f13a4aSGreg Roach				break;
2187a6f13a4aSGreg Roach			case 'CHAN':
2188a6f13a4aSGreg Roach				uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2189a6f13a4aSGreg Roach					return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2190a6f13a4aSGreg Roach				});
2191a6f13a4aSGreg Roach				break;
2192a6f13a4aSGreg Roach			case 'BIRT:DATE':
2193a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2194a6f13a4aSGreg Roach				break;
2195a6f13a4aSGreg Roach			case 'DEAT:DATE':
2196a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2197a6f13a4aSGreg Roach				break;
2198a6f13a4aSGreg Roach			case 'MARR:DATE':
2199a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\\Family::compareMarrDate');
2200a6f13a4aSGreg Roach				break;
2201a6f13a4aSGreg Roach			default:
2202a6f13a4aSGreg Roach				// unsorted or already sorted by SQL
2203a6f13a4aSGreg Roach				break;
2204a6f13a4aSGreg Roach		}
2205a6f13a4aSGreg Roach
2206a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2207a6f13a4aSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($parser) + 1;
2208a6f13a4aSGreg Roach	}
2209a6f13a4aSGreg Roach
2210a6f13a4aSGreg Roach	/**
2211a6f13a4aSGreg Roach	 * XML <List> end element handler
2212a6f13a4aSGreg Roach	 */
2213*8edd1043SGreg Roach	private function listEndHandler() {
2214a6f13a4aSGreg Roach		global $parser, $parserStack, $report;
2215a6f13a4aSGreg Roach
2216a6f13a4aSGreg Roach		$this->process_repeats--;
2217a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
2218a6f13a4aSGreg Roach			return;
2219a6f13a4aSGreg Roach		}
2220a6f13a4aSGreg Roach
2221a6f13a4aSGreg Roach		// Check if there is any list
2222a6f13a4aSGreg Roach		if (count($this->list) > 0) {
2223a6f13a4aSGreg Roach			$lineoffset = 0;
2224a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
2225a6f13a4aSGreg Roach				$lineoffset += $rep[1];
2226a6f13a4aSGreg Roach			}
2227a6f13a4aSGreg Roach			//-- read the xml from the file
2228a6f13a4aSGreg Roach			$lines = file($report);
2229a6f13a4aSGreg Roach			while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<List") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2230a6f13a4aSGreg Roach				$lineoffset--;
2231a6f13a4aSGreg Roach			}
2232a6f13a4aSGreg Roach			$lineoffset++;
2233a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
2234a6f13a4aSGreg Roach			$line_nr   = $lineoffset + $this->repeat_bytes;
2235a6f13a4aSGreg Roach			// List Level counter
2236a6f13a4aSGreg Roach			$count = 1;
2237a6f13a4aSGreg Roach			while (0 < $count) {
2238a6f13a4aSGreg Roach				if (strpos($lines[$line_nr], "<List") !== false) {
2239a6f13a4aSGreg Roach					$count++;
2240a6f13a4aSGreg Roach				} elseif (strpos($lines[$line_nr], "</List") !== false) {
2241a6f13a4aSGreg Roach					$count--;
2242a6f13a4aSGreg Roach				}
2243a6f13a4aSGreg Roach				if (0 < $count) {
2244a6f13a4aSGreg Roach					$reportxml .= $lines[$line_nr];
2245a6f13a4aSGreg Roach				}
2246a6f13a4aSGreg Roach				$line_nr++;
2247a6f13a4aSGreg Roach			}
2248a6f13a4aSGreg Roach			// No need to drag this
2249a6f13a4aSGreg Roach			unset($lines);
2250a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>";
2251a6f13a4aSGreg Roach			// Save original values
2252a6f13a4aSGreg Roach			array_push($parserStack, $parser);
2253a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
2254a6f13a4aSGreg Roach
2255a6f13a4aSGreg Roach			$this->list_total   = count($this->list);
2256a6f13a4aSGreg Roach			$this->list_private = 0;
2257a6f13a4aSGreg Roach			foreach ($this->list as $record) {
2258a6f13a4aSGreg Roach				if ($record->canShow()) {
2259a6f13a4aSGreg Roach					$this->gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
2260a6f13a4aSGreg Roach					//-- start the sax parser
2261a6f13a4aSGreg Roach					$repeat_parser = xml_parser_create();
2262a6f13a4aSGreg Roach					$parser        = $repeat_parser;
2263a6f13a4aSGreg Roach					xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2264a6f13a4aSGreg Roach					xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2265a6f13a4aSGreg Roach					xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2266a6f13a4aSGreg Roach					if (!xml_parse($repeat_parser, $reportxml, true)) {
2267a6f13a4aSGreg Roach						throw new \DomainException(sprintf(
2268a6f13a4aSGreg Roach							'ListEHandler XML error: %s at line %d',
2269a6f13a4aSGreg Roach							xml_error_string(xml_get_error_code($repeat_parser)),
2270a6f13a4aSGreg Roach							xml_get_current_line_number($repeat_parser)
2271a6f13a4aSGreg Roach						));
2272a6f13a4aSGreg Roach					}
2273a6f13a4aSGreg Roach					xml_parser_free($repeat_parser);
2274a6f13a4aSGreg Roach				} else {
2275a6f13a4aSGreg Roach					$this->list_private++;
2276a6f13a4aSGreg Roach				}
2277a6f13a4aSGreg Roach			}
2278a6f13a4aSGreg Roach			$this->list   = array();
2279a6f13a4aSGreg Roach			$parser       = array_pop($parserStack);
2280a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
2281a6f13a4aSGreg Roach		}
2282a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2283a6f13a4aSGreg Roach	}
2284a6f13a4aSGreg Roach
2285a6f13a4aSGreg Roach	/**
2286a6f13a4aSGreg Roach	 * XML <ListTotal> element handler
2287a6f13a4aSGreg Roach	 *
2288a6f13a4aSGreg Roach	 * Prints the total number of records in a list
2289a6f13a4aSGreg Roach	 * The total number is collected from
2290a6f13a4aSGreg Roach	 * List and Relatives
2291a6f13a4aSGreg Roach	 */
2292*8edd1043SGreg Roach	private function listTotalStartHandler() {
2293a6f13a4aSGreg Roach		if ($this->list_private == 0) {
2294a6f13a4aSGreg Roach			$this->current_element->addText($this->list_total);
2295a6f13a4aSGreg Roach		} else {
2296a6f13a4aSGreg Roach			$this->current_element->addText(($this->list_total - $this->list_private) . " / " . $this->list_total);
2297a6f13a4aSGreg Roach		}
2298a6f13a4aSGreg Roach	}
2299a6f13a4aSGreg Roach
2300a6f13a4aSGreg Roach	/**
2301a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
2302a6f13a4aSGreg Roach	 */
2303*8edd1043SGreg Roach	private function relativesStartHandler($attrs) {
2304a6f13a4aSGreg Roach		global $parser, $vars, $WT_TREE;
2305a6f13a4aSGreg Roach
2306a6f13a4aSGreg Roach		$this->process_repeats++;
2307a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
2308a6f13a4aSGreg Roach			return;
2309a6f13a4aSGreg Roach		}
2310a6f13a4aSGreg Roach
2311a6f13a4aSGreg Roach		$sortby = "NAME";
2312a6f13a4aSGreg Roach		if (isset($attrs['sortby'])) {
2313a6f13a4aSGreg Roach			$sortby = $attrs['sortby'];
2314a6f13a4aSGreg Roach		}
2315a6f13a4aSGreg Roach		$match = array();
2316a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $sortby, $match)) {
2317a6f13a4aSGreg Roach			$sortby = $vars[$match[1]]['id'];
2318a6f13a4aSGreg Roach			$sortby = trim($sortby);
2319a6f13a4aSGreg Roach		}
2320a6f13a4aSGreg Roach
2321a6f13a4aSGreg Roach		$maxgen = -1;
2322a6f13a4aSGreg Roach		if (isset($attrs['maxgen'])) {
2323a6f13a4aSGreg Roach			$maxgen = $attrs['maxgen'];
2324a6f13a4aSGreg Roach		}
2325a6f13a4aSGreg Roach		if ($maxgen == "*") {
2326a6f13a4aSGreg Roach			$maxgen = -1;
2327a6f13a4aSGreg Roach		}
2328a6f13a4aSGreg Roach
2329a6f13a4aSGreg Roach		$group = "child-family";
2330a6f13a4aSGreg Roach		if (isset($attrs['group'])) {
2331a6f13a4aSGreg Roach			$group = $attrs['group'];
2332a6f13a4aSGreg Roach		}
2333a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $group, $match)) {
2334a6f13a4aSGreg Roach			$group = $vars[$match[1]]['id'];
2335a6f13a4aSGreg Roach			$group = trim($group);
2336a6f13a4aSGreg Roach		}
2337a6f13a4aSGreg Roach
2338a6f13a4aSGreg Roach		$id = "";
2339a6f13a4aSGreg Roach		if (isset($attrs['id'])) {
2340a6f13a4aSGreg Roach			$id = $attrs['id'];
2341a6f13a4aSGreg Roach		}
2342a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $id, $match)) {
2343a6f13a4aSGreg Roach			$id = $vars[$match[1]]['id'];
2344a6f13a4aSGreg Roach			$id = trim($id);
2345a6f13a4aSGreg Roach		}
2346a6f13a4aSGreg Roach
2347a6f13a4aSGreg Roach		$this->list = array();
2348a6f13a4aSGreg Roach		$person     = Individual::getInstance($id, $WT_TREE);
2349a6f13a4aSGreg Roach		if (!empty($person)) {
2350a6f13a4aSGreg Roach			$this->list[$id] = $person;
2351a6f13a4aSGreg Roach			switch ($group) {
2352a6f13a4aSGreg Roach				case "child-family":
2353a6f13a4aSGreg Roach					foreach ($person->getChildFamilies() as $family) {
2354a6f13a4aSGreg Roach						$husband = $family->getHusband();
2355a6f13a4aSGreg Roach						$wife    = $family->getWife();
2356a6f13a4aSGreg Roach						if (!empty($husband)) {
2357a6f13a4aSGreg Roach							$this->list[$husband->getXref()] = $husband;
2358a6f13a4aSGreg Roach						}
2359a6f13a4aSGreg Roach						if (!empty($wife)) {
2360a6f13a4aSGreg Roach							$this->list[$wife->getXref()] = $wife;
2361a6f13a4aSGreg Roach						}
2362a6f13a4aSGreg Roach						$children = $family->getChildren();
2363a6f13a4aSGreg Roach						foreach ($children as $child) {
2364a6f13a4aSGreg Roach							if (!empty($child)) {
2365a6f13a4aSGreg Roach								$this->list[$child->getXref()] = $child;
2366a6f13a4aSGreg Roach							}
2367a6f13a4aSGreg Roach						}
2368a6f13a4aSGreg Roach					}
2369a6f13a4aSGreg Roach					break;
2370a6f13a4aSGreg Roach				case "spouse-family":
2371a6f13a4aSGreg Roach					foreach ($person->getSpouseFamilies() as $family) {
2372a6f13a4aSGreg Roach						$husband = $family->getHusband();
2373a6f13a4aSGreg Roach						$wife    = $family->getWife();
2374a6f13a4aSGreg Roach						if (!empty($husband)) {
2375a6f13a4aSGreg Roach							$this->list[$husband->getXref()] = $husband;
2376a6f13a4aSGreg Roach						}
2377a6f13a4aSGreg Roach						if (!empty($wife)) {
2378a6f13a4aSGreg Roach							$this->list[$wife->getXref()] = $wife;
2379a6f13a4aSGreg Roach						}
2380a6f13a4aSGreg Roach						$children = $family->getChildren();
2381a6f13a4aSGreg Roach						foreach ($children as $child) {
2382a6f13a4aSGreg Roach							if (!empty($child)) {
2383a6f13a4aSGreg Roach								$this->list[$child->getXref()] = $child;
2384a6f13a4aSGreg Roach							}
2385a6f13a4aSGreg Roach						}
2386a6f13a4aSGreg Roach					}
2387a6f13a4aSGreg Roach					break;
2388a6f13a4aSGreg Roach				case "direct-ancestors":
2389a6f13a4aSGreg Roach					add_ancestors($this->list, $id, false, $maxgen);
2390a6f13a4aSGreg Roach					break;
2391a6f13a4aSGreg Roach				case "ancestors":
2392a6f13a4aSGreg Roach					add_ancestors($this->list, $id, true, $maxgen);
2393a6f13a4aSGreg Roach					break;
2394a6f13a4aSGreg Roach				case "descendants":
2395a6f13a4aSGreg Roach					$this->list[$id]->generation = 1;
2396a6f13a4aSGreg Roach					add_descendancy($this->list, $id, false, $maxgen);
2397a6f13a4aSGreg Roach					break;
2398a6f13a4aSGreg Roach				case "all":
2399a6f13a4aSGreg Roach					add_ancestors($this->list, $id, true, $maxgen);
2400a6f13a4aSGreg Roach					add_descendancy($this->list, $id, true, $maxgen);
2401a6f13a4aSGreg Roach					break;
2402a6f13a4aSGreg Roach			}
2403a6f13a4aSGreg Roach		}
2404a6f13a4aSGreg Roach
2405a6f13a4aSGreg Roach		switch ($sortby) {
2406a6f13a4aSGreg Roach			case 'NAME':
2407a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2408a6f13a4aSGreg Roach				break;
2409a6f13a4aSGreg Roach			case 'BIRT:DATE':
2410a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2411a6f13a4aSGreg Roach				break;
2412a6f13a4aSGreg Roach			case 'DEAT:DATE':
2413a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2414a6f13a4aSGreg Roach				break;
2415a6f13a4aSGreg Roach			case 'generation':
2416a6f13a4aSGreg Roach				$newarray = array();
2417a6f13a4aSGreg Roach				reset($this->list);
2418a6f13a4aSGreg Roach				$genCounter = 1;
2419a6f13a4aSGreg Roach				while (count($newarray) < count($this->list)) {
2420a6f13a4aSGreg Roach					foreach ($this->list as $key => $value) {
2421a6f13a4aSGreg Roach						$this->generation = $value->generation;
2422a6f13a4aSGreg Roach						if ($this->generation == $genCounter) {
2423a6f13a4aSGreg Roach							$newarray[$key]             = new \stdClass;
2424a6f13a4aSGreg Roach							$newarray[$key]->generation = $this->generation;
2425a6f13a4aSGreg Roach						}
2426a6f13a4aSGreg Roach					}
2427a6f13a4aSGreg Roach					$genCounter++;
2428a6f13a4aSGreg Roach				}
2429a6f13a4aSGreg Roach				$this->list = $newarray;
2430a6f13a4aSGreg Roach				break;
2431a6f13a4aSGreg Roach			default:
2432a6f13a4aSGreg Roach				// unsorted
2433a6f13a4aSGreg Roach				break;
2434a6f13a4aSGreg Roach		}
2435a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2436a6f13a4aSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($parser) + 1;
2437a6f13a4aSGreg Roach	}
2438a6f13a4aSGreg Roach
2439a6f13a4aSGreg Roach	/**
2440a6f13a4aSGreg Roach	 * XML </ Relatives> end element handler
2441a6f13a4aSGreg Roach	 */
2442*8edd1043SGreg Roach	private function relativesEndHandler() {
2443a6f13a4aSGreg Roach		global $parser, $parserStack, $report, $WT_TREE;
2444a6f13a4aSGreg Roach
2445a6f13a4aSGreg Roach		$this->process_repeats--;
2446a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
2447a6f13a4aSGreg Roach			return;
2448a6f13a4aSGreg Roach		}
2449a6f13a4aSGreg Roach
2450a6f13a4aSGreg Roach		// Check if there is any relatives
2451a6f13a4aSGreg Roach		if (count($this->list) > 0) {
2452a6f13a4aSGreg Roach			$lineoffset = 0;
2453a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
2454a6f13a4aSGreg Roach				$lineoffset += $rep[1];
2455a6f13a4aSGreg Roach			}
2456a6f13a4aSGreg Roach			//-- read the xml from the file
2457a6f13a4aSGreg Roach			$lines = file($report);
2458a6f13a4aSGreg Roach			while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<Relatives") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2459a6f13a4aSGreg Roach				$lineoffset--;
2460a6f13a4aSGreg Roach			}
2461a6f13a4aSGreg Roach			$lineoffset++;
2462a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
2463a6f13a4aSGreg Roach			$line_nr   = $lineoffset + $this->repeat_bytes;
2464a6f13a4aSGreg Roach			// Relatives Level counter
2465a6f13a4aSGreg Roach			$count = 1;
2466a6f13a4aSGreg Roach			while (0 < $count) {
2467a6f13a4aSGreg Roach				if (strpos($lines[$line_nr], "<Relatives") !== false) {
2468a6f13a4aSGreg Roach					$count++;
2469a6f13a4aSGreg Roach				} elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
2470a6f13a4aSGreg Roach					$count--;
2471a6f13a4aSGreg Roach				}
2472a6f13a4aSGreg Roach				if (0 < $count) {
2473a6f13a4aSGreg Roach					$reportxml .= $lines[$line_nr];
2474a6f13a4aSGreg Roach				}
2475a6f13a4aSGreg Roach				$line_nr++;
2476a6f13a4aSGreg Roach			}
2477a6f13a4aSGreg Roach			// No need to drag this
2478a6f13a4aSGreg Roach			unset($lines);
2479a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>\n";
2480a6f13a4aSGreg Roach			// Save original values
2481a6f13a4aSGreg Roach			array_push($parserStack, $parser);
2482a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
2483a6f13a4aSGreg Roach
2484a6f13a4aSGreg Roach			$this->list_total   = count($this->list);
2485a6f13a4aSGreg Roach			$this->list_private = 0;
2486a6f13a4aSGreg Roach			foreach ($this->list as $key => $value) {
2487a6f13a4aSGreg Roach				if (isset($value->generation)) {
2488a6f13a4aSGreg Roach					$this->generation = $value->generation;
2489a6f13a4aSGreg Roach				}
2490a6f13a4aSGreg Roach				$tmp          = GedcomRecord::getInstance($key, $WT_TREE);
2491a6f13a4aSGreg Roach				$this->gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
2492a6f13a4aSGreg Roach
2493a6f13a4aSGreg Roach				$repeat_parser = xml_parser_create();
2494a6f13a4aSGreg Roach				$parser        = $repeat_parser;
2495a6f13a4aSGreg Roach				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2496a6f13a4aSGreg Roach				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2497a6f13a4aSGreg Roach				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2498a6f13a4aSGreg Roach
2499a6f13a4aSGreg Roach				if (!xml_parse($repeat_parser, $reportxml, true)) {
2500a6f13a4aSGreg Roach					throw new \DomainException(sprintf("RelativesEHandler XML error: %s at line %d", xml_error_string(xml_get_error_code($repeat_parser)), xml_get_current_line_number($repeat_parser)));
2501a6f13a4aSGreg Roach				}
2502a6f13a4aSGreg Roach				xml_parser_free($repeat_parser);
2503a6f13a4aSGreg Roach			}
2504a6f13a4aSGreg Roach			// Clean up the list array
2505a6f13a4aSGreg Roach			$this->list = array();
2506a6f13a4aSGreg Roach			$parser       = array_pop($parserStack);
2507a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
2508a6f13a4aSGreg Roach		}
2509a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2510a6f13a4aSGreg Roach	}
2511a6f13a4aSGreg Roach
2512a6f13a4aSGreg Roach	/**
2513a6f13a4aSGreg Roach	 * XML <Generation /> element handler
2514a6f13a4aSGreg Roach	 *
2515a6f13a4aSGreg Roach	 * Prints the number of generations
2516a6f13a4aSGreg Roach	 */
2517*8edd1043SGreg Roach	private function generationStartHandler() {
2518a6f13a4aSGreg Roach		$this->current_element->addText($this->generation);
2519a6f13a4aSGreg Roach	}
2520a6f13a4aSGreg Roach
2521a6f13a4aSGreg Roach	/**
2522a6f13a4aSGreg Roach	 * XML <NewPage /> element handler
2523a6f13a4aSGreg Roach	 *
2524a6f13a4aSGreg Roach	 * Has to be placed in an element (header, pageheader, body or footer)
2525a6f13a4aSGreg Roach	 */
2526*8edd1043SGreg Roach	private function newPageStartHandler() {
2527a6f13a4aSGreg Roach		global $wt_report;
2528a6f13a4aSGreg Roach
2529a6f13a4aSGreg Roach		$temp = "addpage";
2530a6f13a4aSGreg Roach		$wt_report->addElement($temp);
2531a6f13a4aSGreg Roach	}
2532a6f13a4aSGreg Roach
2533a6f13a4aSGreg Roach	/**
2534a6f13a4aSGreg Roach	 * @param array  $attrs an array of key value pairs for the attributes
2535a6f13a4aSGreg Roach	 * @param string $tag   HTML tag name
2536a6f13a4aSGreg Roach	 */
2537*8edd1043SGreg Roach	private function htmlStartHandler($tag, $attrs) {
2538a6f13a4aSGreg Roach		global $wt_reportStack, $wt_report, $ReportRoot;
2539a6f13a4aSGreg Roach
2540a6f13a4aSGreg Roach		if ($tag === "tempdoc") {
2541a6f13a4aSGreg Roach			return;
2542a6f13a4aSGreg Roach		}
2543a6f13a4aSGreg Roach		array_push($wt_reportStack, $wt_report);
2544a6f13a4aSGreg Roach		$wt_report             = $ReportRoot->createHTML($tag, $attrs);
2545a6f13a4aSGreg Roach		$this->current_element = $wt_report;
2546a6f13a4aSGreg Roach
2547a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
2548a6f13a4aSGreg Roach		$this->print_data = true;
2549a6f13a4aSGreg Roach	}
2550a6f13a4aSGreg Roach
2551a6f13a4aSGreg Roach	/**
2552a6f13a4aSGreg Roach	 * @param string $tag
2553a6f13a4aSGreg Roach	 */
2554*8edd1043SGreg Roach	private function htmlEndHandler($tag) {
2555a6f13a4aSGreg Roach		global $wt_report, $wt_reportStack;
2556a6f13a4aSGreg Roach		if ($tag === "tempdoc") {
2557a6f13a4aSGreg Roach			return;
2558a6f13a4aSGreg Roach		}
2559a6f13a4aSGreg Roach
2560a6f13a4aSGreg Roach		$this->print_data      = array_pop($this->print_data_stack);
2561a6f13a4aSGreg Roach		$this->current_element = $wt_report;
2562a6f13a4aSGreg Roach		$wt_report             = array_pop($wt_reportStack);
2563a6f13a4aSGreg Roach		if (!is_null($wt_report)) {
2564a6f13a4aSGreg Roach			$wt_report->addElement($this->current_element);
2565a6f13a4aSGreg Roach		} else {
2566a6f13a4aSGreg Roach			$wt_report = $this->current_element;
2567a6f13a4aSGreg Roach		}
2568a6f13a4aSGreg Roach	}
2569a6f13a4aSGreg Roach
2570a6f13a4aSGreg Roach	/**
2571a6f13a4aSGreg Roach	 * Handle <Input>
2572a6f13a4aSGreg Roach	 */
2573*8edd1043SGreg Roach	private function inputStartHandler() {
2574a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlStartHandler() being called
2575a6f13a4aSGreg Roach	}
2576a6f13a4aSGreg Roach
2577a6f13a4aSGreg Roach	/**
2578a6f13a4aSGreg Roach	 * Handle </Input>
2579a6f13a4aSGreg Roach	 */
2580*8edd1043SGreg Roach	private function inputEndHandler() {
2581a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlEndHandler() being called
2582a6f13a4aSGreg Roach	}
2583a6f13a4aSGreg Roach
2584a6f13a4aSGreg Roach	/**
2585a6f13a4aSGreg Roach	 * Handle <Report>
2586a6f13a4aSGreg Roach	 */
2587*8edd1043SGreg Roach	private function reportStartHandler() {
2588a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlStartHandler() being called
2589a6f13a4aSGreg Roach	}
2590a6f13a4aSGreg Roach
2591a6f13a4aSGreg Roach	/**
2592a6f13a4aSGreg Roach	 * Handle </Report>
2593a6f13a4aSGreg Roach	 */
2594*8edd1043SGreg Roach	private function reportEndHandler() {
2595a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlEndHandler() being called
2596a6f13a4aSGreg Roach	}
2597a6f13a4aSGreg Roach
2598a6f13a4aSGreg Roach	/**
2599a6f13a4aSGreg Roach	 * XML <titleStartHandler> start element handler
2600a6f13a4aSGreg Roach	 */
2601*8edd1043SGreg Roach	private function titleStartHandler() {
2602a6f13a4aSGreg Roach		$this->report_title = true;
2603a6f13a4aSGreg Roach	}
2604a6f13a4aSGreg Roach
2605a6f13a4aSGreg Roach	/**
2606a6f13a4aSGreg Roach	 * XML </titleEndHandler> end element handler
2607a6f13a4aSGreg Roach	 */
2608*8edd1043SGreg Roach	private function titleEndHandler() {
2609a6f13a4aSGreg Roach		$this->report_title = false;
2610a6f13a4aSGreg Roach	}
2611a6f13a4aSGreg Roach
2612a6f13a4aSGreg Roach	/**
2613a6f13a4aSGreg Roach	 * XML <descriptionStartHandler> start element handler
2614a6f13a4aSGreg Roach	 */
2615*8edd1043SGreg Roach	private function descriptionStartHandler() {
2616a6f13a4aSGreg Roach		$this->report_description = true;
2617a6f13a4aSGreg Roach	}
2618a6f13a4aSGreg Roach
2619a6f13a4aSGreg Roach	/**
2620a6f13a4aSGreg Roach	 * XML </descriptionEndHandler> end element handler
2621a6f13a4aSGreg Roach	 */
2622*8edd1043SGreg Roach	private function descriptionEndHandler() {
2623a6f13a4aSGreg Roach		$this->report_description = false;
2624a6f13a4aSGreg Roach	}
2625a6f13a4aSGreg Roach}
2626