xref: /webtrees/app/Report/ReportParserGenerate.php (revision b0d2e7432246725e6679d0cbf9aed561c689748e)
1a6f13a4aSGreg Roach<?php
2a6f13a4aSGreg Roach/**
3a6f13a4aSGreg Roach * webtrees: online genealogy
4a6f13a4aSGreg Roach * Copyright (C) 2015 webtrees development team
5a6f13a4aSGreg Roach * This program is free software: you can redistribute it and/or modify
6a6f13a4aSGreg Roach * it under the terms of the GNU General Public License as published by
7a6f13a4aSGreg Roach * the Free Software Foundation, either version 3 of the License, or
8a6f13a4aSGreg Roach * (at your option) any later version.
9a6f13a4aSGreg Roach * This program is distributed in the hope that it will be useful,
10a6f13a4aSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
11a6f13a4aSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12a6f13a4aSGreg Roach * GNU General Public License for more details.
13a6f13a4aSGreg Roach * You should have received a copy of the GNU General Public License
14a6f13a4aSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
15a6f13a4aSGreg Roach */
1676692c8bSGreg Roachnamespace Fisharebest\Webtrees\Report;
1776692c8bSGreg Roach
18a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Auth;
19a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Database;
20a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Date;
21a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Family;
223d7a8a4cSGreg Roachuse Fisharebest\Webtrees\Functions\Functions;
233d7a8a4cSGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsDate;
24a6f13a4aSGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
25a6f13a4aSGreg Roachuse Fisharebest\Webtrees\GedcomTag;
26a6f13a4aSGreg Roachuse Fisharebest\Webtrees\I18N;
27a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Individual;
28d1286247SGreg Roachuse Fisharebest\Webtrees\Log;
29a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Media;
30729ce104SGreg Roachuse Fisharebest\Webtrees\Note;
31a6f13a4aSGreg Roachuse Fisharebest\Webtrees\Place;
32a6f13a4aSGreg Roach
33a6f13a4aSGreg Roach/**
34a6f13a4aSGreg Roach * Class ReportParserGenerate - parse a report.xml file and generate the report.
35a6f13a4aSGreg Roach */
36a6f13a4aSGreg Roachclass ReportParserGenerate extends ReportParserBase {
37a6f13a4aSGreg Roach	/** @var bool Are we collecting data from <Footnote> elements  */
38a6f13a4aSGreg Roach	private $process_footnote = true;
39a6f13a4aSGreg Roach
40a6f13a4aSGreg Roach	/** @var bool Are we currently outputing data? */
41a6f13a4aSGreg Roach	private $print_data = false;
42a6f13a4aSGreg Roach
43a6f13a4aSGreg Roach	/** @var bool[] Push-down stack of $print_data */
44a6f13a4aSGreg Roach	private $print_data_stack = array();
45a6f13a4aSGreg Roach
4676692c8bSGreg Roach	/** @var int Are we processing GEDCOM data */
47a6f13a4aSGreg Roach	private $process_gedcoms = 0;
48a6f13a4aSGreg Roach
4976692c8bSGreg Roach	/** @var int Are we processing conditionals */
50a6f13a4aSGreg Roach	private $process_ifs = 0;
51a6f13a4aSGreg Roach
5276692c8bSGreg Roach	/** @var int Are we processing repeats*/
53a6f13a4aSGreg Roach	private $process_repeats = 0;
54a6f13a4aSGreg Roach
55a6f13a4aSGreg Roach	/** @var int Quantity of data to repeat during loops */
56a6f13a4aSGreg Roach	private $repeat_bytes = 0;
57a6f13a4aSGreg Roach
58a6f13a4aSGreg Roach	/** @var array[] Repeated data when iterating over loops */
59a6f13a4aSGreg Roach	private $repeats = array();
60a6f13a4aSGreg Roach
61a6f13a4aSGreg Roach	/** @var array[] Nested repeating data */
62a6f13a4aSGreg Roach	private $repeats_stack = array();
63a6f13a4aSGreg Roach
64e8e7866bSGreg Roach	/** @var ReportBase[] Nested repeating data */
65e8e7866bSGreg Roach	private $wt_report_stack = array();
66e8e7866bSGreg Roach
67e8e7866bSGreg Roach	/** @var resource Nested repeating data */
68e8e7866bSGreg Roach	private $parser;
69e8e7866bSGreg Roach
70e8e7866bSGreg Roach	/** @var resource[] Nested repeating data */
71e8e7866bSGreg Roach	private $parser_stack = array();
72e8e7866bSGreg Roach
73a6f13a4aSGreg Roach	/** @var string The current GEDCOM record */
74a6f13a4aSGreg Roach	private $gedrec = '';
75a6f13a4aSGreg Roach
76a6f13a4aSGreg Roach	/** @var string[] Nested GEDCOM records */
77a6f13a4aSGreg Roach	private $gedrec_stack = array();
78a6f13a4aSGreg Roach
79a6f13a4aSGreg Roach	/** @var ReportBaseElement The currently processed element */
80a6f13a4aSGreg Roach	private $current_element;
81a6f13a4aSGreg Roach
82a6f13a4aSGreg Roach	/** @var ReportBaseElement The currently processed element */
83a6f13a4aSGreg Roach	private $footnote_element;
84a6f13a4aSGreg Roach
85a6f13a4aSGreg Roach	/** @var string The GEDCOM fact currently being processed */
86a6f13a4aSGreg Roach	private $fact = '';
87a6f13a4aSGreg Roach
88a6f13a4aSGreg Roach	/** @var string The GEDCOM value currently being processed */
89a6f13a4aSGreg Roach	private $desc = '';
90a6f13a4aSGreg Roach
91a6f13a4aSGreg Roach	/** @var string The GEDCOM type currently being processed */
92a6f13a4aSGreg Roach	private $type = '';
93a6f13a4aSGreg Roach
94a6f13a4aSGreg Roach	/** @var int The current generational level */
95a6f13a4aSGreg Roach	private $generation = 1;
96a6f13a4aSGreg Roach
97a6f13a4aSGreg Roach	/** @var array Source data for processing lists */
98a6f13a4aSGreg Roach	private $list = array();
99a6f13a4aSGreg Roach
100a6f13a4aSGreg Roach	/** @var int Number of items in lists */
101a6f13a4aSGreg Roach	private $list_total = 0;
102a6f13a4aSGreg Roach
103a6f13a4aSGreg Roach	/** @var int Number of items filtered from lists */
104a6f13a4aSGreg Roach	private $list_private = 0;
105a6f13a4aSGreg Roach
106e8e7866bSGreg Roach	/** @var ReportBase A factory for creating report elements */
107e8e7866bSGreg Roach	private $report_root;
108e8e7866bSGreg Roach
109e8e7866bSGreg Roach	/** @var ReportBase Nested report elements */
110e8e7866bSGreg Roach	private $wt_report;
111e8e7866bSGreg Roach
112d1286247SGreg Roach	/** @var string[][] Variables defined in the report at run-time */
113d1286247SGreg Roach	private $vars;
114d1286247SGreg Roach
11576692c8bSGreg Roach	/**
11676692c8bSGreg Roach	 * Create a parser for a report
11776692c8bSGreg Roach	 *
11876692c8bSGreg Roach	 * @param string     $report     The XML filename
11976692c8bSGreg Roach	 * @param ReportBase $report_root
12076692c8bSGreg Roach	 * @param string[][] $vars
12176692c8bSGreg Roach	 */
12282759250SGreg Roach	public function __construct($report, ReportBase $report_root = null, array $vars = array()) {
123e8e7866bSGreg Roach		$this->report_root     = $report_root;
124e8e7866bSGreg Roach		$this->wt_report       = $report_root;
125a6f13a4aSGreg Roach		$this->current_element = new ReportBaseElement;
126d1286247SGreg Roach		$this->vars            = $vars;
127a6f13a4aSGreg Roach		parent::__construct($report);
128a6f13a4aSGreg Roach	}
129a6f13a4aSGreg Roach
130a6f13a4aSGreg Roach	/**
131a6f13a4aSGreg Roach	 * XML start element handler
132a6f13a4aSGreg Roach	 *
133a6f13a4aSGreg Roach	 * This function is called whenever a starting element is reached
134a6f13a4aSGreg Roach	 * The element handler will be called if found, otherwise it must be HTML
135a6f13a4aSGreg Roach	 *
136a6f13a4aSGreg Roach	 * @param resource $parser the resource handler for the XML parser
137a6f13a4aSGreg Roach	 * @param string   $name   the name of the XML element parsed
138a6f13a4aSGreg Roach	 * @param array    $attrs  an array of key value pairs for the attributes
139a6f13a4aSGreg Roach	 */
1408edd1043SGreg Roach	protected function startElement($parser, $name, $attrs) {
141a6f13a4aSGreg Roach		$newattrs = array();
142a6f13a4aSGreg Roach
143a6f13a4aSGreg Roach		foreach ($attrs as $key => $value) {
144a6f13a4aSGreg Roach			if (preg_match("/^\\$(\w+)$/", $value, $match)) {
145d1286247SGreg Roach				if ((isset($this->vars[$match[1]]['id'])) && (!isset($this->vars[$match[1]]['gedcom']))) {
146d1286247SGreg Roach					$value = $this->vars[$match[1]]['id'];
147a6f13a4aSGreg Roach				}
148a6f13a4aSGreg Roach			}
149a6f13a4aSGreg Roach			$newattrs[$key] = $value;
150a6f13a4aSGreg Roach		}
151a6f13a4aSGreg Roach		$attrs = $newattrs;
152a6f13a4aSGreg 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")) {
153a6f13a4aSGreg Roach			$start_method = $name . 'StartHandler';
154a6f13a4aSGreg Roach			$end_method   = $name . 'EndHandler';
155a6f13a4aSGreg Roach			if (method_exists($this, $start_method)) {
156a6f13a4aSGreg Roach				$this->$start_method($attrs);
157a6f13a4aSGreg Roach			} elseif (!method_exists($this, $end_method)) {
158a6f13a4aSGreg Roach				$this->htmlStartHandler($name, $attrs);
159a6f13a4aSGreg Roach			}
160a6f13a4aSGreg Roach		}
161a6f13a4aSGreg Roach	}
162a6f13a4aSGreg Roach
163a6f13a4aSGreg Roach	/**
164a6f13a4aSGreg Roach	 * XML end element handler
165a6f13a4aSGreg Roach	 *
166a6f13a4aSGreg Roach	 * This function is called whenever an ending element is reached
167a6f13a4aSGreg Roach	 * The element handler will be called if found, otherwise it must be HTML
168a6f13a4aSGreg Roach	 *
169a6f13a4aSGreg Roach	 * @param resource $parser the resource handler for the XML parser
170a6f13a4aSGreg Roach	 * @param string   $name   the name of the XML element parsed
171a6f13a4aSGreg Roach	 */
1728edd1043SGreg Roach	protected function endElement($parser, $name) {
173a6f13a4aSGreg 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")) {
174a6f13a4aSGreg Roach			$start_method = $name . 'StartHandler';
175a6f13a4aSGreg Roach			$end_method   = $name . 'EndHandler';
176a6f13a4aSGreg Roach			if (method_exists($this, $end_method)) {
177a6f13a4aSGreg Roach				$this->$end_method();
178a6f13a4aSGreg Roach			} elseif (!method_exists($this, $start_method)) {
179a6f13a4aSGreg Roach				$this->htmlEndHandler($name);
180a6f13a4aSGreg Roach			}
181a6f13a4aSGreg Roach		}
182a6f13a4aSGreg Roach	}
183a6f13a4aSGreg Roach
184a6f13a4aSGreg Roach	/**
185a6f13a4aSGreg Roach	 * XML character data handler
186a6f13a4aSGreg Roach	 *
187a6f13a4aSGreg Roach	 * @param resource $parser the resource handler for the XML parser
188a6f13a4aSGreg Roach	 * @param string   $data   the name of the XML element parsed
189a6f13a4aSGreg Roach	 */
1908edd1043SGreg Roach	protected function characterData($parser, $data) {
191e8e7866bSGreg Roach		if ($this->print_data && $this->process_gedcoms === 0 && $this->process_ifs === 0 && $this->process_repeats === 0) {
192a6f13a4aSGreg Roach			$this->current_element->addText($data);
193a6f13a4aSGreg Roach		}
194a6f13a4aSGreg Roach	}
195a6f13a4aSGreg Roach
196a6f13a4aSGreg Roach	/**
19776692c8bSGreg Roach	 * XML <style>
198a6f13a4aSGreg Roach	 *
199a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
200a6f13a4aSGreg Roach	 */
2018edd1043SGreg Roach	private function styleStartHandler($attrs) {
202a6f13a4aSGreg Roach		if (empty($attrs['name'])) {
203a6f13a4aSGreg Roach			throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
204a6f13a4aSGreg Roach		}
205a6f13a4aSGreg Roach
206a6f13a4aSGreg Roach		// array Style that will be passed on
207a6f13a4aSGreg Roach		$s = array();
208a6f13a4aSGreg Roach
209a6f13a4aSGreg Roach		// string Name af the style
210a6f13a4aSGreg Roach		$s['name'] = $attrs['name'];
211a6f13a4aSGreg Roach
212a6f13a4aSGreg Roach		// string Name of the DEFAULT font
213e8e7866bSGreg Roach		$s['font'] = $this->wt_report->defaultFont;
214a6f13a4aSGreg Roach		if (!empty($attrs['font'])) {
215a6f13a4aSGreg Roach			$s['font'] = $attrs['font'];
216a6f13a4aSGreg Roach		}
217a6f13a4aSGreg Roach
218a6f13a4aSGreg Roach		// int The size of the font in points
219e8e7866bSGreg Roach		$s['size'] = $this->wt_report->defaultFontSize;
220a6f13a4aSGreg Roach		if (!empty($attrs['size'])) {
221a6f13a4aSGreg Roach			$s['size'] = (int) $attrs['size'];
222a6f13a4aSGreg Roach		} // Get it as int to ignore all decimal points or text (if any text then int(0))
223a6f13a4aSGreg Roach
224a6f13a4aSGreg Roach		// string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
225a6f13a4aSGreg Roach		$s['style'] = "";
226a6f13a4aSGreg Roach		if (!empty($attrs['style'])) {
227a6f13a4aSGreg Roach			$s['style'] = $attrs['style'];
228a6f13a4aSGreg Roach		}
229a6f13a4aSGreg Roach
230e8e7866bSGreg Roach		$this->wt_report->addStyle($s);
231a6f13a4aSGreg Roach	}
232a6f13a4aSGreg Roach
233a6f13a4aSGreg Roach	/**
23476692c8bSGreg Roach	 * XML <Doc>
235a6f13a4aSGreg Roach	 *
236a6f13a4aSGreg Roach	 * Sets up the basics of the document proparties
237a6f13a4aSGreg Roach	 *
238a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
239a6f13a4aSGreg Roach	 */
2408edd1043SGreg Roach	private function docStartHandler($attrs) {
241e8e7866bSGreg Roach		$this->parser = $this->xml_parser;
242a6f13a4aSGreg Roach
243a6f13a4aSGreg Roach		// Custom page width
244a6f13a4aSGreg Roach		if (!empty($attrs['customwidth'])) {
245e8e7866bSGreg Roach			$this->wt_report->pagew = (int) $attrs['customwidth'];
246a6f13a4aSGreg Roach		} // Get it as int to ignore all decimal points or text (if any text then int(0))
247a6f13a4aSGreg Roach		// Custom Page height
248a6f13a4aSGreg Roach		if (!empty($attrs['customheight'])) {
249e8e7866bSGreg Roach			$this->wt_report->pageh = (int) $attrs['customheight'];
250a6f13a4aSGreg Roach		} // Get it as int to ignore all decimal points or text (if any text then int(0))
251a6f13a4aSGreg Roach
252a6f13a4aSGreg Roach		// Left Margin
253a6f13a4aSGreg Roach		if (isset($attrs['leftmargin'])) {
254a6f13a4aSGreg Roach			if ($attrs['leftmargin'] === "0") {
255e8e7866bSGreg Roach				$this->wt_report->leftmargin = 0;
256a6f13a4aSGreg Roach			} elseif (!empty($attrs['leftmargin'])) {
257e8e7866bSGreg Roach				$this->wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
258a6f13a4aSGreg Roach			}
259a6f13a4aSGreg Roach		}
260a6f13a4aSGreg Roach		// Right Margin
261a6f13a4aSGreg Roach		if (isset($attrs['rightmargin'])) {
262a6f13a4aSGreg Roach			if ($attrs['rightmargin'] === "0") {
263e8e7866bSGreg Roach				$this->wt_report->rightmargin = 0;
264a6f13a4aSGreg Roach			} elseif (!empty($attrs['rightmargin'])) {
265e8e7866bSGreg Roach				$this->wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
266a6f13a4aSGreg Roach			}
267a6f13a4aSGreg Roach		}
268a6f13a4aSGreg Roach		// Top Margin
269a6f13a4aSGreg Roach		if (isset($attrs['topmargin'])) {
270a6f13a4aSGreg Roach			if ($attrs['topmargin'] === "0") {
271e8e7866bSGreg Roach				$this->wt_report->topmargin = 0;
272a6f13a4aSGreg Roach			} elseif (!empty($attrs['topmargin'])) {
273e8e7866bSGreg Roach				$this->wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
274a6f13a4aSGreg Roach			}
275a6f13a4aSGreg Roach		}
276a6f13a4aSGreg Roach		// Bottom Margin
277a6f13a4aSGreg Roach		if (isset($attrs['bottommargin'])) {
278a6f13a4aSGreg Roach			if ($attrs['bottommargin'] === "0") {
279e8e7866bSGreg Roach				$this->wt_report->bottommargin = 0;
280a6f13a4aSGreg Roach			} elseif (!empty($attrs['bottommargin'])) {
281e8e7866bSGreg Roach				$this->wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
282a6f13a4aSGreg Roach			}
283a6f13a4aSGreg Roach		}
284a6f13a4aSGreg Roach		// Header Margin
285a6f13a4aSGreg Roach		if (isset($attrs['headermargin'])) {
286a6f13a4aSGreg Roach			if ($attrs['headermargin'] === "0") {
287e8e7866bSGreg Roach				$this->wt_report->headermargin = 0;
288a6f13a4aSGreg Roach			} elseif (!empty($attrs['headermargin'])) {
289e8e7866bSGreg Roach				$this->wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
290a6f13a4aSGreg Roach			}
291a6f13a4aSGreg Roach		}
292a6f13a4aSGreg Roach		// Footer Margin
293a6f13a4aSGreg Roach		if (isset($attrs['footermargin'])) {
294a6f13a4aSGreg Roach			if ($attrs['footermargin'] === "0") {
295e8e7866bSGreg Roach				$this->wt_report->footermargin = 0;
296a6f13a4aSGreg Roach			} elseif (!empty($attrs['footermargin'])) {
297e8e7866bSGreg Roach				$this->wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
298a6f13a4aSGreg Roach			}
299a6f13a4aSGreg Roach		}
300a6f13a4aSGreg Roach
301a6f13a4aSGreg Roach		// Page Orientation
302a6f13a4aSGreg Roach		if (!empty($attrs['orientation'])) {
303a6f13a4aSGreg Roach			if ($attrs['orientation'] == "landscape") {
304e8e7866bSGreg Roach				$this->wt_report->orientation = "landscape";
305a6f13a4aSGreg Roach			} elseif ($attrs['orientation'] == "portrait") {
306e8e7866bSGreg Roach				$this->wt_report->orientation = "portrait";
307a6f13a4aSGreg Roach			}
308a6f13a4aSGreg Roach		}
309a6f13a4aSGreg Roach		// Page Size
310a6f13a4aSGreg Roach		if (!empty($attrs['pageSize'])) {
311e8e7866bSGreg Roach			$this->wt_report->pageFormat = strtoupper($attrs['pageSize']);
312a6f13a4aSGreg Roach		}
313a6f13a4aSGreg Roach
314a6f13a4aSGreg Roach		// Show Generated By...
315a6f13a4aSGreg Roach		if (isset($attrs['showGeneratedBy'])) {
316a6f13a4aSGreg Roach			if ($attrs['showGeneratedBy'] === "0") {
317e8e7866bSGreg Roach				$this->wt_report->showGenText = false;
318a6f13a4aSGreg Roach			} elseif ($attrs['showGeneratedBy'] === "1") {
319e8e7866bSGreg Roach				$this->wt_report->showGenText = true;
320a6f13a4aSGreg Roach			}
321a6f13a4aSGreg Roach		}
322a6f13a4aSGreg Roach
323e8e7866bSGreg Roach		$this->wt_report->setup();
324a6f13a4aSGreg Roach	}
325a6f13a4aSGreg Roach
326a6f13a4aSGreg Roach	/**
32776692c8bSGreg Roach	 * XML </Doc>
328a6f13a4aSGreg Roach	 */
3298edd1043SGreg Roach	private function docEndHandler() {
330e8e7866bSGreg Roach		$this->wt_report->run();
331a6f13a4aSGreg Roach	}
332a6f13a4aSGreg Roach
333a6f13a4aSGreg Roach	/**
33476692c8bSGreg Roach	 * XML <Header>
335a6f13a4aSGreg Roach	 */
3368edd1043SGreg Roach	private function headerStartHandler() {
337a6f13a4aSGreg Roach		// Clear the Header before any new elements are added
338e8e7866bSGreg Roach		$this->wt_report->clearHeader();
339e8e7866bSGreg Roach		$this->wt_report->setProcessing("H");
340a6f13a4aSGreg Roach	}
341a6f13a4aSGreg Roach
342a6f13a4aSGreg Roach	/**
34376692c8bSGreg Roach	 * XML <PageHeader>
344a6f13a4aSGreg Roach	 */
3458edd1043SGreg Roach	private function pageHeaderStartHandler() {
346a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
347a6f13a4aSGreg Roach		$this->print_data = false;
348e8e7866bSGreg Roach		array_push($this->wt_report_stack, $this->wt_report);
349e8e7866bSGreg Roach		$this->wt_report = $this->report_root->createPageHeader();
350a6f13a4aSGreg Roach	}
351a6f13a4aSGreg Roach
352a6f13a4aSGreg Roach	/**
35376692c8bSGreg Roach	 * XML <pageHeaderEndHandler>
354a6f13a4aSGreg Roach	 */
3558edd1043SGreg Roach	private function pageHeaderEndHandler() {
356a6f13a4aSGreg Roach		$this->print_data        = array_pop($this->print_data_stack);
357e8e7866bSGreg Roach		$this->current_element   = $this->wt_report;
358e8e7866bSGreg Roach		$this->wt_report         = array_pop($this->wt_report_stack);
359e8e7866bSGreg Roach		$this->wt_report->addElement($this->current_element);
360a6f13a4aSGreg Roach	}
361a6f13a4aSGreg Roach
362a6f13a4aSGreg Roach	/**
36376692c8bSGreg Roach	 * XML <bodyStartHandler>
364a6f13a4aSGreg Roach	 */
3658edd1043SGreg Roach	private function bodyStartHandler() {
366e8e7866bSGreg Roach		$this->wt_report->setProcessing("B");
367a6f13a4aSGreg Roach	}
368a6f13a4aSGreg Roach
369a6f13a4aSGreg Roach	/**
37076692c8bSGreg Roach	 * XML <footerStartHandler>
371a6f13a4aSGreg Roach	 */
3728edd1043SGreg Roach	private function footerStartHandler() {
373e8e7866bSGreg Roach		$this->wt_report->setProcessing("F");
374a6f13a4aSGreg Roach	}
375a6f13a4aSGreg Roach
376a6f13a4aSGreg Roach	/**
37776692c8bSGreg Roach	 * XML <Cell>
378a6f13a4aSGreg Roach	 *
379a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
380a6f13a4aSGreg Roach	 */
3818edd1043SGreg Roach	private function cellStartHandler($attrs) {
382a6f13a4aSGreg Roach		// string The text alignment of the text in this box.
383a6f13a4aSGreg Roach		$align = "";
384a6f13a4aSGreg Roach		if (!empty($attrs['align'])) {
385a6f13a4aSGreg Roach			$align = $attrs['align'];
386a6f13a4aSGreg Roach			// RTL supported left/right alignment
387a6f13a4aSGreg Roach			if ($align == "rightrtl") {
388e8e7866bSGreg Roach				if ($this->wt_report->rtl) {
389a6f13a4aSGreg Roach					$align = "left";
390a6f13a4aSGreg Roach				} else {
391a6f13a4aSGreg Roach					$align = "right";
392a6f13a4aSGreg Roach				}
393a6f13a4aSGreg Roach			} elseif ($align == "leftrtl") {
394e8e7866bSGreg Roach				if ($this->wt_report->rtl) {
395a6f13a4aSGreg Roach					$align = "right";
396a6f13a4aSGreg Roach				} else {
397a6f13a4aSGreg Roach					$align = "left";
398a6f13a4aSGreg Roach				}
399a6f13a4aSGreg Roach			}
400a6f13a4aSGreg Roach		}
401a6f13a4aSGreg Roach
402a6f13a4aSGreg Roach		// string The color to fill the background of this cell
403a6f13a4aSGreg Roach		$bgcolor = "";
404a6f13a4aSGreg Roach		if (!empty($attrs['bgcolor'])) {
405a6f13a4aSGreg Roach			$bgcolor = $attrs['bgcolor'];
406a6f13a4aSGreg Roach		}
407a6f13a4aSGreg Roach
408a6f13a4aSGreg Roach		// int Whether or not the background should be painted
409a6f13a4aSGreg Roach		$fill = 1;
410a6f13a4aSGreg Roach		if (isset($attrs['fill'])) {
411a6f13a4aSGreg Roach			if ($attrs['fill'] === "0") {
412a6f13a4aSGreg Roach				$fill = 0;
413a6f13a4aSGreg Roach			} elseif ($attrs['fill'] === "1") {
414a6f13a4aSGreg Roach				$fill = 1;
415a6f13a4aSGreg Roach			}
416a6f13a4aSGreg Roach		}
417a6f13a4aSGreg Roach
418a6f13a4aSGreg Roach		$reseth = true;
419a6f13a4aSGreg Roach		// boolean   if true reset the last cell height (default true)
420a6f13a4aSGreg Roach		if (isset($attrs['reseth'])) {
421a6f13a4aSGreg Roach			if ($attrs['reseth'] === "0") {
422a6f13a4aSGreg Roach				$reseth = false;
423a6f13a4aSGreg Roach			} elseif ($attrs['reseth'] === "1") {
424a6f13a4aSGreg Roach				$reseth = true;
425a6f13a4aSGreg Roach			}
426a6f13a4aSGreg Roach		}
427a6f13a4aSGreg Roach
428a6f13a4aSGreg Roach		// mixed Whether or not a border should be printed around this box
429a6f13a4aSGreg Roach		$border = 0;
430a6f13a4aSGreg Roach		if (!empty($attrs['border'])) {
431a6f13a4aSGreg Roach			$border = $attrs['border'];
432a6f13a4aSGreg Roach		}
433a6f13a4aSGreg Roach		// string Border color in HTML code
434a6f13a4aSGreg Roach		$bocolor = "";
435a6f13a4aSGreg Roach		if (!empty($attrs['bocolor'])) {
436a6f13a4aSGreg Roach			$bocolor = $attrs['bocolor'];
437a6f13a4aSGreg Roach		}
438a6f13a4aSGreg Roach
439a6f13a4aSGreg Roach		// int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
440a6f13a4aSGreg Roach		$height = 0;
441a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
442a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
443a6f13a4aSGreg Roach		}
444a6f13a4aSGreg 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.
445a6f13a4aSGreg Roach		$width = 0;
446a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
447a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
448a6f13a4aSGreg Roach		}
449a6f13a4aSGreg Roach
450a6f13a4aSGreg Roach		// int Stretch carachter mode
451a6f13a4aSGreg Roach		$stretch = 0;
452a6f13a4aSGreg Roach		if (!empty($attrs['stretch'])) {
453a6f13a4aSGreg Roach			$stretch = (int) $attrs['stretch'];
454a6f13a4aSGreg Roach		}
455a6f13a4aSGreg Roach
456a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. The default is the current position.
457a6f13a4aSGreg Roach		$left = ".";
458a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
459a6f13a4aSGreg Roach			if ($attrs['left'] === ".") {
460a6f13a4aSGreg Roach				$left = ".";
461a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
462a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
463a6f13a4aSGreg Roach			} elseif ($attrs['left'] === "0") {
464a6f13a4aSGreg Roach				$left = 0;
465a6f13a4aSGreg Roach			}
466a6f13a4aSGreg Roach		}
467a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
468a6f13a4aSGreg Roach		$top = ".";
469a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
470a6f13a4aSGreg Roach			if ($attrs['top'] === ".") {
471a6f13a4aSGreg Roach				$top = ".";
472a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
473a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
474a6f13a4aSGreg Roach			} elseif ($attrs['top'] === "0") {
475a6f13a4aSGreg Roach				$top = 0;
476a6f13a4aSGreg Roach			}
477a6f13a4aSGreg Roach		}
478a6f13a4aSGreg Roach
479a6f13a4aSGreg Roach		// string The name of the Style that should be used to render the text.
480a6f13a4aSGreg Roach		$style = "";
481a6f13a4aSGreg Roach		if (!empty($attrs['style'])) {
482a6f13a4aSGreg Roach			$style = $attrs['style'];
483a6f13a4aSGreg Roach		}
484a6f13a4aSGreg Roach
485a6f13a4aSGreg Roach		// string Text color in html code
486a6f13a4aSGreg Roach		$tcolor = "";
487a6f13a4aSGreg Roach		if (!empty($attrs['tcolor'])) {
488a6f13a4aSGreg Roach			$tcolor = $attrs['tcolor'];
489a6f13a4aSGreg Roach		}
490a6f13a4aSGreg Roach
491a6f13a4aSGreg Roach		// int Indicates where the current position should go after the call.
492a6f13a4aSGreg Roach		$ln = 0;
493a6f13a4aSGreg Roach		if (isset($attrs['newline'])) {
494a6f13a4aSGreg Roach			if (!empty($attrs['newline'])) {
495a6f13a4aSGreg Roach				$ln = (int) $attrs['newline'];
496a6f13a4aSGreg Roach			} elseif ($attrs['newline'] === "0") {
497a6f13a4aSGreg Roach				$ln = 0;
498a6f13a4aSGreg Roach			}
499a6f13a4aSGreg Roach		}
500a6f13a4aSGreg Roach
501a6f13a4aSGreg Roach		if ($align == "left") {
502a6f13a4aSGreg Roach			$align = "L";
503a6f13a4aSGreg Roach		} elseif ($align == "right") {
504a6f13a4aSGreg Roach			$align = "R";
505a6f13a4aSGreg Roach		} elseif ($align == "center") {
506a6f13a4aSGreg Roach			$align = "C";
507a6f13a4aSGreg Roach		} elseif ($align == "justify") {
508a6f13a4aSGreg Roach			$align = "J";
509a6f13a4aSGreg Roach		}
510a6f13a4aSGreg Roach
511a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
512a6f13a4aSGreg Roach		$this->print_data = true;
513a6f13a4aSGreg Roach
514e8e7866bSGreg Roach		$this->current_element = $this->report_root->createCell(
515a6f13a4aSGreg Roach			$width,
516a6f13a4aSGreg Roach			$height,
517a6f13a4aSGreg Roach			$border,
518a6f13a4aSGreg Roach			$align,
519a6f13a4aSGreg Roach			$bgcolor,
520a6f13a4aSGreg Roach			$style,
521a6f13a4aSGreg Roach			$ln,
522a6f13a4aSGreg Roach			$top,
523a6f13a4aSGreg Roach			$left,
524a6f13a4aSGreg Roach			$fill,
525a6f13a4aSGreg Roach			$stretch,
526a6f13a4aSGreg Roach			$bocolor,
527a6f13a4aSGreg Roach			$tcolor,
528a6f13a4aSGreg Roach			$reseth
529a6f13a4aSGreg Roach		);
530a6f13a4aSGreg Roach	}
531a6f13a4aSGreg Roach
532a6f13a4aSGreg Roach	/**
53376692c8bSGreg Roach	 * XML </Cell>
534a6f13a4aSGreg Roach	 */
5358edd1043SGreg Roach	private function cellEndHandler() {
536a6f13a4aSGreg Roach		$this->print_data = array_pop($this->print_data_stack);
537e8e7866bSGreg Roach		$this->wt_report->addElement($this->current_element);
538a6f13a4aSGreg Roach	}
539a6f13a4aSGreg Roach
540a6f13a4aSGreg Roach	/**
541a6f13a4aSGreg Roach	 * XML <Now /> element handler
542a6f13a4aSGreg Roach	 */
5438edd1043SGreg Roach	private function nowStartHandler() {
5443d7a8a4cSGreg Roach		$g = FunctionsDate::timestampToGedcomDate(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	 */
5518edd1043SGreg 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	 */
5588edd1043SGreg 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	 */
5678edd1043SGreg Roach	private function gedcomStartHandler($attrs) {
568d1286247SGreg Roach		global $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)) {
589d1286247SGreg Roach					if (isset($this->vars[$match[1]]['gedcom'])) {
590d1286247SGreg Roach						$newgedrec = $this->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;
6093d7a8a4cSGreg Roach						$newgedrec = Functions::getSubRecord($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	 */
6308edd1043SGreg 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	/**
63976692c8bSGreg Roach	 * XML <textBoxStartHandler>
640a6f13a4aSGreg Roach	 *
641a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
642a6f13a4aSGreg Roach	 */
6438edd1043SGreg Roach	private function textBoxStartHandler($attrs) {
644a6f13a4aSGreg Roach		// string Background color code
645a6f13a4aSGreg Roach		$bgcolor = "";
646a6f13a4aSGreg Roach		if (!empty($attrs['bgcolor'])) {
647a6f13a4aSGreg Roach			$bgcolor = $attrs['bgcolor'];
648a6f13a4aSGreg Roach		}
649a6f13a4aSGreg Roach
650a6f13a4aSGreg Roach		// boolean Wether or not fill the background color
651a6f13a4aSGreg Roach		$fill = true;
652a6f13a4aSGreg Roach		if (isset($attrs['fill'])) {
653a6f13a4aSGreg Roach			if ($attrs['fill'] === "0") {
654a6f13a4aSGreg Roach				$fill = false;
655a6f13a4aSGreg Roach			} elseif ($attrs['fill'] === "1") {
656a6f13a4aSGreg Roach				$fill = true;
657a6f13a4aSGreg Roach			}
658a6f13a4aSGreg Roach		}
659a6f13a4aSGreg Roach
660a6f13a4aSGreg Roach		// var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
661a6f13a4aSGreg Roach		$border = false;
662a6f13a4aSGreg Roach		if (isset($attrs['border'])) {
663a6f13a4aSGreg Roach			if ($attrs['border'] === "1") {
664a6f13a4aSGreg Roach				$border = true;
665a6f13a4aSGreg Roach			} elseif ($attrs['border'] === "0") {
666a6f13a4aSGreg Roach				$border = false;
667a6f13a4aSGreg Roach			}
668a6f13a4aSGreg Roach		}
669a6f13a4aSGreg Roach
670a6f13a4aSGreg Roach		// int The starting height of this cell. If the text wraps the height will automatically be adjusted
671a6f13a4aSGreg Roach		$height = 0;
672a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
673a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
674a6f13a4aSGreg Roach		}
675a6f13a4aSGreg Roach		// int Setting the width to 0 will make it the width from the current location to the margin
676a6f13a4aSGreg Roach		$width = 0;
677a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
678a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
679a6f13a4aSGreg Roach		}
680a6f13a4aSGreg Roach
681a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. The default is the current position.
682a6f13a4aSGreg Roach		$left = ".";
683a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
684a6f13a4aSGreg Roach			if ($attrs['left'] === ".") {
685a6f13a4aSGreg Roach				$left = ".";
686a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
687a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
688a6f13a4aSGreg Roach			} elseif ($attrs['left'] === "0") {
689a6f13a4aSGreg Roach				$left = 0;
690a6f13a4aSGreg Roach			}
691a6f13a4aSGreg Roach		}
692a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
693a6f13a4aSGreg Roach		$top = ".";
694a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
695a6f13a4aSGreg Roach			if ($attrs['top'] === ".") {
696a6f13a4aSGreg Roach				$top = ".";
697a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
698a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
699a6f13a4aSGreg Roach			} elseif ($attrs['top'] === "0") {
700a6f13a4aSGreg Roach				$top = 0;
701a6f13a4aSGreg Roach			}
702a6f13a4aSGreg Roach		}
703a6f13a4aSGreg 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
704a6f13a4aSGreg Roach		$newline = false;
705a6f13a4aSGreg Roach		if (isset($attrs['newline'])) {
706a6f13a4aSGreg Roach			if ($attrs['newline'] === "1") {
707a6f13a4aSGreg Roach				$newline = true;
708a6f13a4aSGreg Roach			} elseif ($attrs['newline'] === "0") {
709a6f13a4aSGreg Roach				$newline = false;
710a6f13a4aSGreg Roach			}
711a6f13a4aSGreg Roach		}
712a6f13a4aSGreg Roach		// boolean
713a6f13a4aSGreg Roach		$pagecheck = true;
714a6f13a4aSGreg Roach		if (isset($attrs['pagecheck'])) {
715a6f13a4aSGreg Roach			if ($attrs['pagecheck'] === "0") {
716a6f13a4aSGreg Roach				$pagecheck = false;
717a6f13a4aSGreg Roach			} elseif ($attrs['pagecheck'] === "1") {
718a6f13a4aSGreg Roach				$pagecheck = true;
719a6f13a4aSGreg Roach			}
720a6f13a4aSGreg Roach		}
721a6f13a4aSGreg Roach		// boolean Cell padding
722a6f13a4aSGreg Roach		$padding = true;
723a6f13a4aSGreg Roach		if (isset($attrs['padding'])) {
724a6f13a4aSGreg Roach			if ($attrs['padding'] === "0") {
725a6f13a4aSGreg Roach				$padding = false;
726a6f13a4aSGreg Roach			} elseif ($attrs['padding'] === "1") {
727a6f13a4aSGreg Roach				$padding = true;
728a6f13a4aSGreg Roach			}
729a6f13a4aSGreg Roach		}
730a6f13a4aSGreg Roach		// boolean Reset this box Height
731a6f13a4aSGreg Roach		$reseth = false;
732a6f13a4aSGreg Roach		if (isset($attrs['reseth'])) {
733a6f13a4aSGreg Roach			if ($attrs['reseth'] === "1") {
734a6f13a4aSGreg Roach				$reseth = true;
735a6f13a4aSGreg Roach			} elseif ($attrs['reseth'] === "0") {
736a6f13a4aSGreg Roach				$reseth = false;
737a6f13a4aSGreg Roach			}
738a6f13a4aSGreg Roach		}
739a6f13a4aSGreg Roach
740a6f13a4aSGreg Roach		// string Style of rendering
741a6f13a4aSGreg Roach		$style = "";
742a6f13a4aSGreg Roach
743a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
744a6f13a4aSGreg Roach		$this->print_data = false;
745a6f13a4aSGreg Roach
746e8e7866bSGreg Roach		array_push($this->wt_report_stack, $this->wt_report);
747e8e7866bSGreg Roach		$this->wt_report = $this->report_root->createTextBox(
748a6f13a4aSGreg Roach			$width,
749a6f13a4aSGreg Roach			$height,
750a6f13a4aSGreg Roach			$border,
751a6f13a4aSGreg Roach			$bgcolor,
752a6f13a4aSGreg Roach			$newline,
753a6f13a4aSGreg Roach			$left,
754a6f13a4aSGreg Roach			$top,
755a6f13a4aSGreg Roach			$pagecheck,
756a6f13a4aSGreg Roach			$style,
757a6f13a4aSGreg Roach			$fill,
758a6f13a4aSGreg Roach			$padding,
759a6f13a4aSGreg Roach			$reseth
760a6f13a4aSGreg Roach		);
761a6f13a4aSGreg Roach	}
762a6f13a4aSGreg Roach
763a6f13a4aSGreg Roach	/**
76476692c8bSGreg Roach	 * XML <textBoxEndHandler>
765a6f13a4aSGreg Roach	 */
7668edd1043SGreg Roach	private function textBoxEndHandler() {
767a6f13a4aSGreg Roach		$this->print_data      = array_pop($this->print_data_stack);
768e8e7866bSGreg Roach		$this->current_element = $this->wt_report;
769e8e7866bSGreg Roach		$this->wt_report       = array_pop($this->wt_report_stack);
770e8e7866bSGreg Roach		$this->wt_report->addElement($this->current_element);
771a6f13a4aSGreg Roach	}
772a6f13a4aSGreg Roach
773a6f13a4aSGreg Roach	/**
77476692c8bSGreg Roach	 * XLM <Text>.
77576692c8bSGreg Roach	 *
776a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
777a6f13a4aSGreg Roach	 */
7788edd1043SGreg Roach	private function textStartHandler($attrs) {
779a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
780a6f13a4aSGreg Roach		$this->print_data = true;
781a6f13a4aSGreg Roach
782a6f13a4aSGreg Roach		// string The name of the Style that should be used to render the text.
783a6f13a4aSGreg Roach		$style = "";
784a6f13a4aSGreg Roach		if (!empty($attrs['style'])) {
785a6f13a4aSGreg Roach			$style = $attrs['style'];
786a6f13a4aSGreg Roach		}
787a6f13a4aSGreg Roach
788a6f13a4aSGreg Roach		// string  The color of the text - Keep the black color as default
789a6f13a4aSGreg Roach		$color = "";
790a6f13a4aSGreg Roach		if (!empty($attrs['color'])) {
791a6f13a4aSGreg Roach			$color = $attrs['color'];
792a6f13a4aSGreg Roach		}
793a6f13a4aSGreg Roach
794e8e7866bSGreg Roach		$this->current_element = $this->report_root->createText($style, $color);
795a6f13a4aSGreg Roach	}
796a6f13a4aSGreg Roach
797a6f13a4aSGreg Roach	/**
79876692c8bSGreg Roach	 * XML </Text>
799a6f13a4aSGreg Roach	 */
8008edd1043SGreg Roach	private function textEndHandler() {
801a6f13a4aSGreg Roach		$this->print_data = array_pop($this->print_data_stack);
802e8e7866bSGreg Roach		$this->wt_report->addElement($this->current_element);
803a6f13a4aSGreg Roach	}
804a6f13a4aSGreg Roach
805a6f13a4aSGreg Roach	/**
80676692c8bSGreg Roach	 * XML <GetPersonName/>
80776692c8bSGreg Roach	 *
808a6f13a4aSGreg Roach	 * Get the name
809a6f13a4aSGreg Roach	 * 1. id is empty - current GEDCOM record
810a6f13a4aSGreg Roach	 * 2. id is set with a record id
811a6f13a4aSGreg Roach	 *
812a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
813a6f13a4aSGreg Roach	 */
8148edd1043SGreg Roach	private function getPersonNameStartHandler($attrs) {
815d1286247SGreg Roach		global $WT_TREE;
816a6f13a4aSGreg Roach
817a6f13a4aSGreg Roach		$id    = "";
818a6f13a4aSGreg Roach		$match = array();
819a6f13a4aSGreg Roach		if (empty($attrs['id'])) {
820a6f13a4aSGreg Roach			if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
821a6f13a4aSGreg Roach				$id = $match[1];
822a6f13a4aSGreg Roach			}
823a6f13a4aSGreg Roach		} else {
824a6f13a4aSGreg Roach			if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
825d1286247SGreg Roach				if (isset($this->vars[$match[1]]['id'])) {
826d1286247SGreg Roach					$id = $this->vars[$match[1]]['id'];
827a6f13a4aSGreg Roach				}
828a6f13a4aSGreg Roach			} else {
829a6f13a4aSGreg Roach				if (preg_match("/@(.+)/", $attrs['id'], $match)) {
830a6f13a4aSGreg Roach					$gmatch = array();
831a6f13a4aSGreg Roach					if (preg_match("/\d $match[1] @([^@]+)@/", $this->gedrec, $gmatch)) {
832a6f13a4aSGreg Roach						$id = $gmatch[1];
833a6f13a4aSGreg Roach					}
834a6f13a4aSGreg Roach				} else {
835a6f13a4aSGreg Roach					$id = $attrs['id'];
836a6f13a4aSGreg Roach				}
837a6f13a4aSGreg Roach			}
838a6f13a4aSGreg Roach		}
839a6f13a4aSGreg Roach		if (!empty($id)) {
840a6f13a4aSGreg Roach			$record = GedcomRecord::getInstance($id, $WT_TREE);
841a6f13a4aSGreg Roach			if (is_null($record)) {
842a6f13a4aSGreg Roach				return;
843a6f13a4aSGreg Roach			}
844a6f13a4aSGreg Roach			if (!$record->canShowName()) {
845a6f13a4aSGreg Roach				$this->current_element->addText(I18N::translate('Private'));
846a6f13a4aSGreg Roach			} else {
847a6f13a4aSGreg Roach				$name = $record->getFullName();
848a6f13a4aSGreg Roach				$name = preg_replace(
849a6f13a4aSGreg Roach					array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
850a6f13a4aSGreg Roach					array('«', '', '»'),
851a6f13a4aSGreg Roach					$name
852a6f13a4aSGreg Roach				);
853a6f13a4aSGreg Roach				$name = strip_tags($name);
854a6f13a4aSGreg Roach				if (!empty($attrs['truncate'])) {
855a6f13a4aSGreg Roach					if (mb_strlen($name) > $attrs['truncate']) {
856a6f13a4aSGreg Roach						$name  = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
857a6f13a4aSGreg Roach						$words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
858a6f13a4aSGreg Roach						$name  = $words[count($words) - 1];
859a6f13a4aSGreg Roach						for ($i = count($words) - 2; $i >= 0; $i--) {
860a6f13a4aSGreg Roach							$len = mb_strlen($name);
861a6f13a4aSGreg Roach							for ($j = count($words) - 3; $j >= 0; $j--) {
862a6f13a4aSGreg Roach								$len += mb_strlen($words[$j]);
863a6f13a4aSGreg Roach							}
864a6f13a4aSGreg Roach							if ($len > $attrs['truncate']) {
865a6f13a4aSGreg Roach								$first_letter = mb_substr($words[$i], 0, 1);
866a6f13a4aSGreg Roach								// Do not show " of nick-names
867a6f13a4aSGreg Roach								if ($first_letter != "\"") {
868a6f13a4aSGreg Roach									$name = mb_substr($words[$i], 0, 1) . '. ' . $name;
869a6f13a4aSGreg Roach								}
870a6f13a4aSGreg Roach							} else {
871a6f13a4aSGreg Roach								$name = $words[$i] . ' ' . $name;
872a6f13a4aSGreg Roach							}
873a6f13a4aSGreg Roach						}
874a6f13a4aSGreg Roach					}
875a6f13a4aSGreg Roach				} else {
876a6f13a4aSGreg Roach					$addname = $record->getAddName();
877a6f13a4aSGreg Roach					$addname = preg_replace(
878a6f13a4aSGreg Roach						array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
879a6f13a4aSGreg Roach						array('«', '', '»'),
880a6f13a4aSGreg Roach						$addname
881a6f13a4aSGreg Roach					);
882a6f13a4aSGreg Roach					$addname = strip_tags($addname);
883a6f13a4aSGreg Roach					if (!empty($addname)) {
884a6f13a4aSGreg Roach						$name .= " " . $addname;
885a6f13a4aSGreg Roach					}
886a6f13a4aSGreg Roach				}
887a6f13a4aSGreg Roach				$this->current_element->addText(trim($name));
888a6f13a4aSGreg Roach			}
889a6f13a4aSGreg Roach		}
890a6f13a4aSGreg Roach	}
891a6f13a4aSGreg Roach
892a6f13a4aSGreg Roach	/**
89376692c8bSGreg Roach	 * XML <GedcomValue/>
894a6f13a4aSGreg Roach	 *
895a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
896a6f13a4aSGreg Roach	 */
8978edd1043SGreg Roach	private function gedcomValueStartHandler($attrs) {
898a6f13a4aSGreg Roach		global $WT_TREE;
899a6f13a4aSGreg Roach
900a6f13a4aSGreg Roach		$id    = "";
901a6f13a4aSGreg Roach		$match = array();
902a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
903a6f13a4aSGreg Roach			$id = $match[1];
904a6f13a4aSGreg Roach		}
905a6f13a4aSGreg Roach
906a6f13a4aSGreg Roach		if (isset($attrs['newline']) && $attrs['newline'] == "1") {
907a6f13a4aSGreg Roach			$useBreak = "1";
908a6f13a4aSGreg Roach		} else {
909a6f13a4aSGreg Roach			$useBreak = "0";
910a6f13a4aSGreg Roach		}
911a6f13a4aSGreg Roach
912a6f13a4aSGreg Roach		$tag = $attrs['tag'];
913a6f13a4aSGreg Roach		if (!empty($tag)) {
914a6f13a4aSGreg Roach			if ($tag == "@desc") {
915a6f13a4aSGreg Roach				$value = $this->desc;
916a6f13a4aSGreg Roach				$value = trim($value);
917a6f13a4aSGreg Roach				$this->current_element->addText($value);
918a6f13a4aSGreg Roach			}
919a6f13a4aSGreg Roach			if ($tag == "@id") {
920a6f13a4aSGreg Roach				$this->current_element->addText($id);
921a6f13a4aSGreg Roach			} else {
922a6f13a4aSGreg Roach				$tag = str_replace("@fact", $this->fact, $tag);
923a6f13a4aSGreg Roach				if (empty($attrs['level'])) {
924a6f13a4aSGreg Roach					$temp  = explode(" ", trim($this->gedrec));
925a6f13a4aSGreg Roach					$level = $temp[0];
926a6f13a4aSGreg Roach					if ($level == 0) {
927a6f13a4aSGreg Roach						$level++;
928a6f13a4aSGreg Roach					}
929a6f13a4aSGreg Roach				} else {
930a6f13a4aSGreg Roach					$level = $attrs['level'];
931a6f13a4aSGreg Roach				}
932a6f13a4aSGreg Roach				$tags  = preg_split('/[: ]/', $tag);
9333d7a8a4cSGreg Roach				$value = $this->getGedcomValue($tag, $level, $this->gedrec);
934a6f13a4aSGreg Roach				switch (end($tags)) {
935a6f13a4aSGreg Roach					case 'DATE':
936a6f13a4aSGreg Roach						$tmp   = new Date($value);
937a6f13a4aSGreg Roach						$value = $tmp->display();
938a6f13a4aSGreg Roach						break;
939a6f13a4aSGreg Roach					case 'PLAC':
940a6f13a4aSGreg Roach						$tmp   = new Place($value, $WT_TREE);
941a6f13a4aSGreg Roach						$value = $tmp->getShortName();
942a6f13a4aSGreg Roach						break;
943a6f13a4aSGreg Roach				}
944a6f13a4aSGreg Roach				if ($useBreak == "1") {
945a6f13a4aSGreg Roach					// Insert <br> when multiple dates exist.
946a6f13a4aSGreg Roach					// This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
947a6f13a4aSGreg Roach					$value = str_replace('(', '<br>(', $value);
948a6f13a4aSGreg Roach					$value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
949a6f13a4aSGreg Roach					$value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
950a6f13a4aSGreg Roach					if (substr($value, 0, 6) == '<br>') {
951a6f13a4aSGreg Roach						$value = substr($value, 6);
952a6f13a4aSGreg Roach					}
953a6f13a4aSGreg Roach				}
954a6f13a4aSGreg Roach				$this->current_element->addText($value);
955a6f13a4aSGreg Roach			}
956a6f13a4aSGreg Roach		}
957a6f13a4aSGreg Roach	}
958a6f13a4aSGreg Roach
959a6f13a4aSGreg Roach	/**
96076692c8bSGreg Roach	 * XML <RepeatTag>
961a6f13a4aSGreg Roach	 *
962a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
963a6f13a4aSGreg Roach	 */
9648edd1043SGreg Roach	private function repeatTagStartHandler($attrs) {
965a6f13a4aSGreg Roach		$this->process_repeats++;
966a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
967a6f13a4aSGreg Roach			return;
968a6f13a4aSGreg Roach		}
969a6f13a4aSGreg Roach
970a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
971a6f13a4aSGreg Roach		$this->repeats      = array();
972e8e7866bSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($this->parser);
973a6f13a4aSGreg Roach
974a6f13a4aSGreg Roach		$tag = "";
975a6f13a4aSGreg Roach		if (isset($attrs['tag'])) {
976a6f13a4aSGreg Roach			$tag = $attrs['tag'];
977a6f13a4aSGreg Roach		}
978a6f13a4aSGreg Roach		if (!empty($tag)) {
979a6f13a4aSGreg Roach			if ($tag == "@desc") {
980a6f13a4aSGreg Roach				$value = $this->desc;
981a6f13a4aSGreg Roach				$value = trim($value);
982a6f13a4aSGreg Roach				$this->current_element->addText($value);
983a6f13a4aSGreg Roach			} else {
984a6f13a4aSGreg Roach				$tag   = str_replace("@fact", $this->fact, $tag);
985a6f13a4aSGreg Roach				$tags  = explode(":", $tag);
986a6f13a4aSGreg Roach				$temp  = explode(" ", trim($this->gedrec));
987a6f13a4aSGreg Roach				$level = $temp[0];
988a6f13a4aSGreg Roach				if ($level == 0) {
989a6f13a4aSGreg Roach					$level++;
990a6f13a4aSGreg Roach				}
991a6f13a4aSGreg Roach				$subrec = $this->gedrec;
992a6f13a4aSGreg Roach				$t      = $tag;
993a6f13a4aSGreg Roach				$count  = count($tags);
994a6f13a4aSGreg Roach				$i      = 0;
995a6f13a4aSGreg Roach				while ($i < $count) {
996a6f13a4aSGreg Roach					$t = $tags[$i];
997a6f13a4aSGreg Roach					if (!empty($t)) {
998a6f13a4aSGreg Roach						if ($i < ($count - 1)) {
9993d7a8a4cSGreg Roach							$subrec = Functions::getSubRecord($level, "$level $t", $subrec);
1000a6f13a4aSGreg Roach							if (empty($subrec)) {
1001a6f13a4aSGreg Roach								$level--;
10023d7a8a4cSGreg Roach								$subrec = Functions::getSubRecord($level, "@ $t", $this->gedrec);
1003a6f13a4aSGreg Roach								if (empty($subrec)) {
1004a6f13a4aSGreg Roach									return;
1005a6f13a4aSGreg Roach								}
1006a6f13a4aSGreg Roach							}
1007a6f13a4aSGreg Roach						}
1008a6f13a4aSGreg Roach						$level++;
1009a6f13a4aSGreg Roach					}
1010a6f13a4aSGreg Roach					$i++;
1011a6f13a4aSGreg Roach				}
1012a6f13a4aSGreg Roach				$level--;
1013a6f13a4aSGreg Roach				$count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
1014a6f13a4aSGreg Roach				$i     = 0;
1015a6f13a4aSGreg Roach				while ($i < $count) {
10163d7a8a4cSGreg Roach					$this->repeats[] = Functions::getSubRecord($level, "$level $t", $subrec, $i + 1);
1017a6f13a4aSGreg Roach					$i++;
1018a6f13a4aSGreg Roach				}
1019a6f13a4aSGreg Roach			}
1020a6f13a4aSGreg Roach		}
1021a6f13a4aSGreg Roach	}
1022a6f13a4aSGreg Roach
1023a6f13a4aSGreg Roach	/**
102476692c8bSGreg Roach	 * XML </ RepeatTag>
1025a6f13a4aSGreg Roach	 */
10268edd1043SGreg Roach	private function repeatTagEndHandler() {
1027e8e7866bSGreg Roach		global $report;
1028a6f13a4aSGreg Roach
1029a6f13a4aSGreg Roach		$this->process_repeats--;
1030a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
1031a6f13a4aSGreg Roach			return;
1032a6f13a4aSGreg Roach		}
1033a6f13a4aSGreg Roach
1034a6f13a4aSGreg Roach		// Check if there is anything to repeat
1035a6f13a4aSGreg Roach		if (count($this->repeats) > 0) {
1036a6f13a4aSGreg Roach			// No need to load them if not used...
1037a6f13a4aSGreg Roach
1038a6f13a4aSGreg Roach			$lineoffset = 0;
1039a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
1040a6f13a4aSGreg Roach				$lineoffset += $rep[1];
1041a6f13a4aSGreg Roach			}
1042a6f13a4aSGreg Roach			//-- read the xml from the file
1043a6f13a4aSGreg Roach			$lines = file($report);
1044a6f13a4aSGreg Roach			while (strpos($lines[$lineoffset + $this->repeat_bytes], "<RepeatTag") === false) {
1045a6f13a4aSGreg Roach				$lineoffset--;
1046a6f13a4aSGreg Roach			}
1047a6f13a4aSGreg Roach			$lineoffset++;
1048a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
1049a6f13a4aSGreg Roach			$line_nr   = $lineoffset + $this->repeat_bytes;
1050a6f13a4aSGreg Roach			// RepeatTag Level counter
1051a6f13a4aSGreg Roach			$count = 1;
1052a6f13a4aSGreg Roach			while (0 < $count) {
1053a6f13a4aSGreg Roach				if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
1054a6f13a4aSGreg Roach					$count++;
1055a6f13a4aSGreg Roach				} elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
1056a6f13a4aSGreg Roach					$count--;
1057a6f13a4aSGreg Roach				}
1058a6f13a4aSGreg Roach				if (0 < $count) {
1059a6f13a4aSGreg Roach					$reportxml .= $lines[$line_nr];
1060a6f13a4aSGreg Roach				}
1061a6f13a4aSGreg Roach				$line_nr++;
1062a6f13a4aSGreg Roach			}
1063a6f13a4aSGreg Roach			// No need to drag this
1064a6f13a4aSGreg Roach			unset($lines);
1065a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>\n";
1066a6f13a4aSGreg Roach			// Save original values
1067e8e7866bSGreg Roach			array_push($this->parser_stack, $this->parser);
1068a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
1069a6f13a4aSGreg Roach			foreach ($this->repeats as $gedrec) {
1070a6f13a4aSGreg Roach				$this->gedrec  = $gedrec;
1071a6f13a4aSGreg Roach				$repeat_parser = xml_parser_create();
1072e8e7866bSGreg Roach				$this->parser  = $repeat_parser;
1073a6f13a4aSGreg Roach				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1074a6f13a4aSGreg Roach				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1075a6f13a4aSGreg Roach				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1076a6f13a4aSGreg Roach				if (!xml_parse($repeat_parser, $reportxml, true)) {
1077a6f13a4aSGreg Roach					throw new \DomainException(sprintf(
1078a6f13a4aSGreg Roach						'RepeatTagEHandler XML error: %s at line %d',
1079a6f13a4aSGreg Roach						xml_error_string(xml_get_error_code($repeat_parser)),
1080a6f13a4aSGreg Roach						xml_get_current_line_number($repeat_parser)
1081a6f13a4aSGreg Roach					));
1082a6f13a4aSGreg Roach				}
1083a6f13a4aSGreg Roach				xml_parser_free($repeat_parser);
1084a6f13a4aSGreg Roach			}
1085a6f13a4aSGreg Roach			// Restore original values
1086a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
1087e8e7866bSGreg Roach			$this->parser = array_pop($this->parser_stack);
1088a6f13a4aSGreg Roach		}
1089a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1090a6f13a4aSGreg Roach	}
1091a6f13a4aSGreg Roach
1092a6f13a4aSGreg Roach	/**
1093a6f13a4aSGreg Roach	 * Variable lookup
1094a6f13a4aSGreg Roach	 *
1095a6f13a4aSGreg Roach	 * Retrieve predefined variables :
1096a6f13a4aSGreg Roach	 *
1097a6f13a4aSGreg Roach	 * @ desc GEDCOM fact description, example:
1098a6f13a4aSGreg Roach	 *        1 EVEN This is a description
1099a6f13a4aSGreg Roach	 * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
1100a6f13a4aSGreg Roach	 * $ I18N::translate('....')
1101a6f13a4aSGreg Roach	 * $ language_settings[]
1102a6f13a4aSGreg Roach	 *
1103a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1104a6f13a4aSGreg Roach	 */
11058edd1043SGreg Roach	private function varStartHandler($attrs) {
1106a6f13a4aSGreg Roach		if (empty($attrs['var'])) {
1107e8e7866bSGreg 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($this->parser));
1108a6f13a4aSGreg Roach		}
1109a6f13a4aSGreg Roach
1110a6f13a4aSGreg Roach		$var = $attrs['var'];
1111a6f13a4aSGreg Roach		// SetVar element preset variables
1112d1286247SGreg Roach		if (!empty($this->vars[$var]['id'])) {
1113d1286247SGreg Roach			$var = $this->vars[$var]['id'];
1114a6f13a4aSGreg Roach		} else {
1115a6f13a4aSGreg Roach			$tfact = $this->fact;
1116a6f13a4aSGreg Roach			if (($this->fact === "EVEN" || $this->fact === "FACT") && $this->type !== " ") {
1117a6f13a4aSGreg Roach				// Use :
1118a6f13a4aSGreg Roach				// n TYPE This text if string
1119a6f13a4aSGreg Roach				$tfact = $this->type;
1120a6f13a4aSGreg Roach			}
1121a6f13a4aSGreg Roach			$var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $this->desc), $var);
1122a6f13a4aSGreg Roach			if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
1123a6f13a4aSGreg Roach				$var = I18N::number($match[1]);
1124a6f13a4aSGreg Roach			} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
1125a6f13a4aSGreg Roach				$var = I18N::translate($match[1]);
1126a6f13a4aSGreg Roach			} elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
1127a6f13a4aSGreg Roach				$var = I18N::translateContext($match[1], $match[2]);
1128a6f13a4aSGreg Roach			}
1129a6f13a4aSGreg Roach		}
1130a6f13a4aSGreg Roach		// Check if variable is set as a date and reformat the date
1131a6f13a4aSGreg Roach		if (isset($attrs['date'])) {
1132a6f13a4aSGreg Roach			if ($attrs['date'] === "1") {
1133a6f13a4aSGreg Roach				$g   = new Date($var);
1134a6f13a4aSGreg Roach				$var = $g->display();
1135a6f13a4aSGreg Roach			}
1136a6f13a4aSGreg Roach		}
1137a6f13a4aSGreg Roach		$this->current_element->addText($var);
11382836aa05SGreg Roach		$this->text = $var; // Used for title/descriptio
1139a6f13a4aSGreg Roach	}
1140a6f13a4aSGreg Roach
1141a6f13a4aSGreg Roach	/**
114276692c8bSGreg Roach	 * XML <Facts>
114376692c8bSGreg Roach	 *
1144a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1145a6f13a4aSGreg Roach	 */
11468edd1043SGreg Roach	private function factsStartHandler($attrs) {
1147d1286247SGreg Roach		global $WT_TREE;
1148a6f13a4aSGreg Roach
1149a6f13a4aSGreg Roach		$this->process_repeats++;
1150a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
1151a6f13a4aSGreg Roach			return;
1152a6f13a4aSGreg Roach		}
1153a6f13a4aSGreg Roach
1154a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
1155a6f13a4aSGreg Roach		$this->repeats      = array();
1156e8e7866bSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($this->parser);
1157a6f13a4aSGreg Roach
1158a6f13a4aSGreg Roach		$id    = "";
1159a6f13a4aSGreg Roach		$match = array();
1160a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1161a6f13a4aSGreg Roach			$id = $match[1];
1162a6f13a4aSGreg Roach		}
1163a6f13a4aSGreg Roach		$tag = "";
1164a6f13a4aSGreg Roach		if (isset($attrs['ignore'])) {
1165a6f13a4aSGreg Roach			$tag .= $attrs['ignore'];
1166a6f13a4aSGreg Roach		}
1167a6f13a4aSGreg Roach		if (preg_match("/\\$(.+)/", $tag, $match)) {
1168d1286247SGreg Roach			$tag = $this->vars[$match[1]]['id'];
1169a6f13a4aSGreg Roach		}
1170a6f13a4aSGreg Roach
1171a6f13a4aSGreg Roach		$record = GedcomRecord::getInstance($id, $WT_TREE);
1172a6f13a4aSGreg Roach		if (empty($attrs['diff']) && !empty($id)) {
1173a6f13a4aSGreg Roach			$facts = $record->getFacts();
11743d7a8a4cSGreg Roach			Functions::sortFacts($facts);
1175a6f13a4aSGreg Roach			$this->repeats  = array();
1176a6f13a4aSGreg Roach			$nonfacts       = explode(',', $tag);
1177a6f13a4aSGreg Roach			foreach ($facts as $event) {
1178a6f13a4aSGreg Roach				if (!in_array($event->getTag(), $nonfacts)) {
1179a6f13a4aSGreg Roach					$this->repeats[] = $event->getGedcom();
1180a6f13a4aSGreg Roach				}
1181a6f13a4aSGreg Roach			}
1182a6f13a4aSGreg Roach		} else {
1183a6f13a4aSGreg Roach			foreach ($record->getFacts() as $fact) {
1184a6f13a4aSGreg Roach				if ($fact->isPendingAddition() && $fact->getTag() !== 'CHAN') {
1185a6f13a4aSGreg Roach					$this->repeats[] = $fact->getGedcom();
1186a6f13a4aSGreg Roach				}
1187a6f13a4aSGreg Roach			}
1188a6f13a4aSGreg Roach		}
1189a6f13a4aSGreg Roach	}
1190a6f13a4aSGreg Roach
1191a6f13a4aSGreg Roach	/**
119276692c8bSGreg Roach	 * XML </Facts>
1193a6f13a4aSGreg Roach	 */
11948edd1043SGreg Roach	private function factsEndHandler() {
1195e8e7866bSGreg Roach		global $report;
1196a6f13a4aSGreg Roach
1197a6f13a4aSGreg Roach		$this->process_repeats--;
1198a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
1199a6f13a4aSGreg Roach			return;
1200a6f13a4aSGreg Roach		}
1201a6f13a4aSGreg Roach
1202a6f13a4aSGreg Roach		// Check if there is anything to repeat
1203a6f13a4aSGreg Roach		if (count($this->repeats) > 0) {
1204a6f13a4aSGreg Roach
1205e8e7866bSGreg Roach			$line       = xml_get_current_line_number($this->parser) - 1;
1206a6f13a4aSGreg Roach			$lineoffset = 0;
1207a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
1208a6f13a4aSGreg Roach				$lineoffset += $rep[1];
1209a6f13a4aSGreg Roach			}
1210a6f13a4aSGreg Roach
1211a6f13a4aSGreg Roach			//-- read the xml from the file
1212a6f13a4aSGreg Roach			$lines = file($report);
1213a6f13a4aSGreg Roach			while ($lineoffset + $this->repeat_bytes > 0 && strpos($lines[$lineoffset + $this->repeat_bytes], '<Facts ') === false) {
1214a6f13a4aSGreg Roach				$lineoffset--;
1215a6f13a4aSGreg Roach			}
1216a6f13a4aSGreg Roach			$lineoffset++;
1217a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
1218a6f13a4aSGreg Roach			$i         = $line + $lineoffset;
1219a6f13a4aSGreg Roach			$line_nr   = $this->repeat_bytes + $lineoffset;
1220a6f13a4aSGreg Roach			while ($line_nr < $i) {
1221a6f13a4aSGreg Roach				$reportxml .= $lines[$line_nr];
1222a6f13a4aSGreg Roach				$line_nr++;
1223a6f13a4aSGreg Roach			}
1224a6f13a4aSGreg Roach			// No need to drag this
1225a6f13a4aSGreg Roach			unset($lines);
1226a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>\n";
1227a6f13a4aSGreg Roach			// Save original values
1228e8e7866bSGreg Roach			array_push($this->parser_stack, $this->parser);
1229a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
1230a6f13a4aSGreg Roach			$count     = count($this->repeats);
1231a6f13a4aSGreg Roach			$i         = 0;
1232a6f13a4aSGreg Roach			while ($i < $count) {
1233a6f13a4aSGreg Roach				$this->gedrec = $this->repeats[$i];
1234a6f13a4aSGreg Roach				$this->fact   = '';
1235a6f13a4aSGreg Roach				$this->desc   = '';
1236a6f13a4aSGreg Roach				if (preg_match('/1 (\w+)(.*)/', $this->gedrec, $match)) {
1237a6f13a4aSGreg Roach					$this->fact = $match[1];
1238a6f13a4aSGreg Roach					if ($this->fact === 'EVEN' || $this->fact === 'FACT') {
1239a6f13a4aSGreg Roach						$tmatch = array();
1240a6f13a4aSGreg Roach						if (preg_match('/2 TYPE (.+)/', $this->gedrec, $tmatch)) {
1241a6f13a4aSGreg Roach							$this->type = trim($tmatch[1]);
1242a6f13a4aSGreg Roach						} else {
1243a6f13a4aSGreg Roach							$this->type = ' ';
1244a6f13a4aSGreg Roach						}
1245a6f13a4aSGreg Roach					}
1246a6f13a4aSGreg Roach					$this->desc = trim($match[2]);
12473d7a8a4cSGreg Roach					$this->desc .= Functions::getCont(2, $this->gedrec);
1248a6f13a4aSGreg Roach				}
1249a6f13a4aSGreg Roach				$repeat_parser = xml_parser_create();
1250e8e7866bSGreg Roach				$this->parser  = $repeat_parser;
1251a6f13a4aSGreg Roach				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1252a6f13a4aSGreg Roach				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1253a6f13a4aSGreg Roach				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1254a6f13a4aSGreg Roach				if (!xml_parse($repeat_parser, $reportxml, true)) {
1255a6f13a4aSGreg Roach					throw new \DomainException(sprintf(
1256a6f13a4aSGreg Roach						'FactsEHandler XML error: %s at line %d',
1257a6f13a4aSGreg Roach						xml_error_string(xml_get_error_code($repeat_parser)),
1258a6f13a4aSGreg Roach						xml_get_current_line_number($repeat_parser)
1259a6f13a4aSGreg Roach					));
1260a6f13a4aSGreg Roach				}
1261a6f13a4aSGreg Roach				xml_parser_free($repeat_parser);
1262a6f13a4aSGreg Roach				$i++;
1263a6f13a4aSGreg Roach			}
1264a6f13a4aSGreg Roach			// Restore original values
1265e8e7866bSGreg Roach			$this->parser = array_pop($this->parser_stack);
1266a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
1267a6f13a4aSGreg Roach		}
1268a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1269a6f13a4aSGreg Roach	}
1270a6f13a4aSGreg Roach
1271a6f13a4aSGreg Roach	/**
1272a6f13a4aSGreg Roach	 * Setting upp or changing variables in the XML
1273d1286247SGreg Roach	 * The XML variable name and value is stored in $this->vars
1274a6f13a4aSGreg Roach	 *
1275a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1276a6f13a4aSGreg Roach	 */
12778edd1043SGreg Roach	private function setVarStartHandler($attrs) {
1278a6f13a4aSGreg Roach		if (empty($attrs['name'])) {
1279a6f13a4aSGreg Roach			throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
1280a6f13a4aSGreg Roach		}
1281a6f13a4aSGreg Roach
1282a6f13a4aSGreg Roach		$name  = $attrs['name'];
1283a6f13a4aSGreg Roach		$value = $attrs['value'];
1284a6f13a4aSGreg Roach		$match = array();
1285a6f13a4aSGreg Roach		// Current GEDCOM record strings
1286a6f13a4aSGreg Roach		if ($value == "@ID") {
1287a6f13a4aSGreg Roach			if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1288a6f13a4aSGreg Roach				$value = $match[1];
1289a6f13a4aSGreg Roach			}
1290a6f13a4aSGreg Roach		} elseif ($value == "@fact") {
1291a6f13a4aSGreg Roach			$value = $this->fact;
1292a6f13a4aSGreg Roach		} elseif ($value == "@desc") {
1293a6f13a4aSGreg Roach			$value = $this->desc;
1294a6f13a4aSGreg Roach		} elseif ($value == "@generation") {
1295a6f13a4aSGreg Roach			$value = $this->generation;
1296a6f13a4aSGreg Roach		} elseif (preg_match("/@(\w+)/", $value, $match)) {
1297a6f13a4aSGreg Roach			$gmatch = array();
1298a6f13a4aSGreg Roach			if (preg_match("/\d $match[1] (.+)/", $this->gedrec, $gmatch)) {
1299a6f13a4aSGreg Roach				$value = str_replace("@", "", trim($gmatch[1]));
1300a6f13a4aSGreg Roach			}
1301a6f13a4aSGreg Roach		}
1302a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $name, $match)) {
1303d1286247SGreg Roach			$name = $this->vars["'" . $match[1] . "'"]['id'];
1304a6f13a4aSGreg Roach		}
1305a6f13a4aSGreg Roach		$count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
1306a6f13a4aSGreg Roach		$i     = 0;
1307a6f13a4aSGreg Roach		while ($i < $count) {
1308d1286247SGreg Roach			$t     = $this->vars[$match[$i][1]]['id'];
1309a6f13a4aSGreg Roach			$value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
1310a6f13a4aSGreg Roach			$i++;
1311a6f13a4aSGreg Roach		}
1312a6f13a4aSGreg Roach		if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
1313a6f13a4aSGreg Roach			$value = I18N::number($match[1]);
1314a6f13a4aSGreg Roach		} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
1315a6f13a4aSGreg Roach			$value = I18N::translate($match[1]);
1316a6f13a4aSGreg Roach		} elseif (preg_match('/^I18N::translate_c\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
1317a6f13a4aSGreg Roach			$value = I18N::translateContext($match[1], $match[2]);
1318a6f13a4aSGreg Roach		}
1319a6f13a4aSGreg Roach		// Arithmetic functions
1320a6f13a4aSGreg Roach		if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
1321a6f13a4aSGreg Roach			switch ($match[2]) {
1322a6f13a4aSGreg Roach				case "+":
1323a6f13a4aSGreg Roach					$t     = $match[1] + $match[3];
1324a6f13a4aSGreg Roach					$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1325a6f13a4aSGreg Roach					break;
1326a6f13a4aSGreg Roach				case "-":
1327a6f13a4aSGreg Roach					$t     = $match[1] - $match[3];
1328a6f13a4aSGreg Roach					$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1329a6f13a4aSGreg Roach					break;
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			}
1339a6f13a4aSGreg Roach		}
1340a6f13a4aSGreg Roach		if (strpos($value, "@") !== false) {
1341a6f13a4aSGreg Roach			$value = "";
1342a6f13a4aSGreg Roach		}
1343d1286247SGreg Roach		$this->vars[$name]['id'] = $value;
1344a6f13a4aSGreg Roach	}
1345a6f13a4aSGreg Roach
1346a6f13a4aSGreg Roach	/**
1347a6f13a4aSGreg Roach	 * XML <if > start element
1348a6f13a4aSGreg Roach	 *
1349a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1350a6f13a4aSGreg Roach	 */
13518edd1043SGreg Roach	private function ifStartHandler($attrs) {
1352a6f13a4aSGreg Roach		if ($this->process_ifs > 0) {
1353a6f13a4aSGreg Roach			$this->process_ifs++;
1354a6f13a4aSGreg Roach
1355a6f13a4aSGreg Roach			return;
1356a6f13a4aSGreg Roach		}
1357a6f13a4aSGreg Roach
1358a6f13a4aSGreg Roach		$condition = $attrs['condition'];
135982759250SGreg Roach		$condition = $this->substituteVars($condition, true);
1360a6f13a4aSGreg Roach		$condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
1361a6f13a4aSGreg Roach		// Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
1362a6f13a4aSGreg Roach		$condition = str_replace("@fact", $this->fact, $condition);
1363a6f13a4aSGreg Roach		$match     = array();
1364a6f13a4aSGreg Roach		$count     = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
1365a6f13a4aSGreg Roach		$i         = 0;
1366a6f13a4aSGreg Roach		while ($i < $count) {
1367a6f13a4aSGreg Roach			$id    = $match[$i][1];
1368a6f13a4aSGreg Roach			$value = '""';
1369a6f13a4aSGreg Roach			if ($id == "ID") {
1370a6f13a4aSGreg Roach				if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1371a6f13a4aSGreg Roach					$value = "'" . $match[1] . "'";
1372a6f13a4aSGreg Roach				}
1373a6f13a4aSGreg Roach			} elseif ($id === "fact") {
1374a6f13a4aSGreg Roach				$value = '"' . $this->fact . '"';
1375a6f13a4aSGreg Roach			} elseif ($id === "desc") {
1376a6f13a4aSGreg Roach				$value = '"' . addslashes($this->desc) . '"';
1377a6f13a4aSGreg Roach			} elseif ($id === "generation") {
1378a6f13a4aSGreg Roach				$value = '"' . $this->generation . '"';
1379a6f13a4aSGreg Roach			} else {
1380a6f13a4aSGreg Roach
1381a6f13a4aSGreg Roach				$temp  = explode(" ", trim($this->gedrec));
1382a6f13a4aSGreg Roach				$level = $temp[0];
1383a6f13a4aSGreg Roach				if ($level == 0) {
1384a6f13a4aSGreg Roach					$level++;
1385a6f13a4aSGreg Roach				}
13863d7a8a4cSGreg Roach				$value = $this->getGedcomValue($id, $level, $this->gedrec);
1387a6f13a4aSGreg Roach				if (empty($value)) {
1388a6f13a4aSGreg Roach					$level++;
13893d7a8a4cSGreg Roach					$value = $this->getGedcomValue($id, $level, $this->gedrec);
1390a6f13a4aSGreg Roach				}
1391a6f13a4aSGreg Roach				$value = preg_replace("/^@(" . WT_REGEX_XREF . ")@$/", "$1", $value);
1392a6f13a4aSGreg Roach				$value = "\"" . addslashes($value) . "\"";
1393a6f13a4aSGreg Roach			}
1394a6f13a4aSGreg Roach			$condition = str_replace("@$id", $value, $condition);
1395a6f13a4aSGreg Roach			$i++;
1396a6f13a4aSGreg Roach		}
1397a6f13a4aSGreg Roach		$condition = "return (bool) ($condition);";
1398a6f13a4aSGreg Roach		$ret       = @eval($condition);
1399a6f13a4aSGreg Roach		if (!$ret) {
1400a6f13a4aSGreg Roach			$this->process_ifs++;
1401a6f13a4aSGreg Roach		}
1402a6f13a4aSGreg Roach	}
1403a6f13a4aSGreg Roach
1404a6f13a4aSGreg Roach	/**
1405a6f13a4aSGreg Roach	 * XML <if /> end element
1406a6f13a4aSGreg Roach	 */
14078edd1043SGreg Roach	private function ifEndHandler() {
1408a6f13a4aSGreg Roach		if ($this->process_ifs > 0) {
1409a6f13a4aSGreg Roach			$this->process_ifs--;
1410a6f13a4aSGreg Roach		}
1411a6f13a4aSGreg Roach	}
1412a6f13a4aSGreg Roach
1413a6f13a4aSGreg Roach	/**
1414a6f13a4aSGreg Roach	 * XML <Footnote > start element
1415a6f13a4aSGreg Roach	 * Collect the Footnote links
1416a6f13a4aSGreg Roach	 * GEDCOM Records that are protected by Privacy setting will be ignore
1417a6f13a4aSGreg Roach	 *
1418a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1419a6f13a4aSGreg Roach	 */
14208edd1043SGreg Roach	private function footnoteStartHandler($attrs) {
1421e8e7866bSGreg Roach		global $WT_TREE;
1422a6f13a4aSGreg Roach
1423a6f13a4aSGreg Roach		$id = "";
1424a6f13a4aSGreg Roach		if (preg_match("/[0-9] (.+) @(.+)@/", $this->gedrec, $match)) {
1425a6f13a4aSGreg Roach			$id = $match[2];
1426a6f13a4aSGreg Roach		}
1427a6f13a4aSGreg Roach		$record = GedcomRecord::GetInstance($id, $WT_TREE);
1428a6f13a4aSGreg Roach		if ($record && $record->canShow()) {
1429a6f13a4aSGreg Roach			array_push($this->print_data_stack, $this->print_data);
1430a6f13a4aSGreg Roach			$this->print_data = true;
1431a6f13a4aSGreg Roach			$style            = "";
1432a6f13a4aSGreg Roach			if (!empty($attrs['style'])) {
1433a6f13a4aSGreg Roach				$style = $attrs['style'];
1434a6f13a4aSGreg Roach			}
1435a6f13a4aSGreg Roach			$this->footnote_element = $this->current_element;
1436e8e7866bSGreg Roach			$this->current_element  = $this->report_root->createFootnote($style);
1437a6f13a4aSGreg Roach		} else {
1438a6f13a4aSGreg Roach			$this->print_data       = false;
1439a6f13a4aSGreg Roach			$this->process_footnote = false;
1440a6f13a4aSGreg Roach		}
1441a6f13a4aSGreg Roach	}
1442a6f13a4aSGreg Roach
1443a6f13a4aSGreg Roach	/**
1444a6f13a4aSGreg Roach	 * XML <Footnote /> end element
1445a6f13a4aSGreg Roach	 * Print the collected Footnote data
1446a6f13a4aSGreg Roach	 */
14478edd1043SGreg Roach	private function footnoteEndHandler() {
1448a6f13a4aSGreg Roach		if ($this->process_footnote) {
1449a6f13a4aSGreg Roach			$this->print_data = array_pop($this->print_data_stack);
1450a6f13a4aSGreg Roach			$temp             = trim($this->current_element->getValue());
1451a6f13a4aSGreg Roach			if (strlen($temp) > 3) {
1452e8e7866bSGreg Roach				$this->wt_report->addElement($this->current_element);
1453a6f13a4aSGreg Roach			}
1454a6f13a4aSGreg Roach			$this->current_element = $this->footnote_element;
1455a6f13a4aSGreg Roach		} else {
1456a6f13a4aSGreg Roach			$this->process_footnote = true;
1457a6f13a4aSGreg Roach		}
1458a6f13a4aSGreg Roach	}
1459a6f13a4aSGreg Roach
1460a6f13a4aSGreg Roach	/**
1461a6f13a4aSGreg Roach	 * XML <FootnoteTexts /> element
1462a6f13a4aSGreg Roach	 */
14638edd1043SGreg Roach	private function footnoteTextsStartHandler() {
1464a6f13a4aSGreg Roach		$temp = "footnotetexts";
1465e8e7866bSGreg Roach		$this->wt_report->addElement($temp);
1466a6f13a4aSGreg Roach	}
1467a6f13a4aSGreg Roach
1468a6f13a4aSGreg Roach	/**
1469a6f13a4aSGreg Roach	 * XML <AgeAtDeath /> element handler
1470a6f13a4aSGreg Roach	 */
14718edd1043SGreg Roach	private function ageAtDeathStartHandler() {
14723d7a8a4cSGreg Roach		// This duplicates functionality in FunctionsPrint::format_fact_date()
1473a6f13a4aSGreg Roach		global $factrec, $WT_TREE;
1474a6f13a4aSGreg Roach
1475a6f13a4aSGreg Roach		$match = array();
1476a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1477a6f13a4aSGreg Roach			$person = Individual::getInstance($match[1], $WT_TREE);
1478a6f13a4aSGreg Roach			// Recorded age
1479a6f13a4aSGreg Roach			if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
1480a6f13a4aSGreg Roach				$fact_age = $match[1];
1481a6f13a4aSGreg Roach			} else {
1482a6f13a4aSGreg Roach				$fact_age = '';
1483a6f13a4aSGreg Roach			}
1484a6f13a4aSGreg Roach			if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
1485a6f13a4aSGreg Roach				$husb_age = $match[1];
1486a6f13a4aSGreg Roach			} else {
1487a6f13a4aSGreg Roach				$husb_age = '';
1488a6f13a4aSGreg Roach			}
1489a6f13a4aSGreg Roach			if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
1490a6f13a4aSGreg Roach				$wife_age = $match[1];
1491a6f13a4aSGreg Roach			} else {
1492a6f13a4aSGreg Roach				$wife_age = '';
1493a6f13a4aSGreg Roach			}
1494a6f13a4aSGreg Roach
1495a6f13a4aSGreg Roach			// Calculated age
1496a6f13a4aSGreg Roach			$birth_date = $person->getBirthDate();
1497a6f13a4aSGreg Roach			// Can't use getDeathDate(), as this also gives BURI/CREM events, which
1498a6f13a4aSGreg Roach			// wouldn't give the correct "days after death" result for people with
1499a6f13a4aSGreg Roach			// no DEAT.
1500a6f13a4aSGreg Roach			$death_event = $person->getFirstFact('DEAT');
1501a6f13a4aSGreg Roach			if ($death_event) {
1502a6f13a4aSGreg Roach				$death_date = $death_event->getDate();
1503a6f13a4aSGreg Roach			} else {
1504a6f13a4aSGreg Roach				$death_date = new Date('');
1505a6f13a4aSGreg Roach			}
1506a6f13a4aSGreg Roach			$value = '';
1507a6f13a4aSGreg Roach			if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
1508a6f13a4aSGreg Roach				$age = Date::getAgeGedcom($birth_date, $death_date);
1509a6f13a4aSGreg Roach				// Only show calculated age if it differs from recorded age
1510a6f13a4aSGreg Roach				if ($age != '' && $age != "0d") {
1511a6f13a4aSGreg 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
1512a6f13a4aSGreg Roach					) {
15133d7a8a4cSGreg Roach						$value  = FunctionsDate::getAgeAtEvent($age, false);
1514a6f13a4aSGreg Roach						$abbrev = substr($value, 0, strpos($value, ' ') + 5);
1515a6f13a4aSGreg Roach						if ($value !== $abbrev) {
1516a6f13a4aSGreg Roach							$value = $abbrev . '.';
1517a6f13a4aSGreg Roach						}
1518a6f13a4aSGreg Roach					}
1519a6f13a4aSGreg Roach				}
1520a6f13a4aSGreg Roach			}
1521a6f13a4aSGreg Roach			$this->current_element->addText($value);
1522a6f13a4aSGreg Roach		}
1523a6f13a4aSGreg Roach	}
1524a6f13a4aSGreg Roach
1525a6f13a4aSGreg Roach	/**
1526a6f13a4aSGreg Roach	 * XML element Forced line break handler - HTML code
1527a6f13a4aSGreg Roach	 */
15288edd1043SGreg Roach	private function brStartHandler() {
1529a6f13a4aSGreg Roach		if ($this->print_data && $this->process_gedcoms === 0) {
1530a6f13a4aSGreg Roach			$this->current_element->addText('<br>');
1531a6f13a4aSGreg Roach		}
1532a6f13a4aSGreg Roach	}
1533a6f13a4aSGreg Roach
1534a6f13a4aSGreg Roach	/**
1535a6f13a4aSGreg Roach	 * XML <sp />element Forced space handler
1536a6f13a4aSGreg Roach	 */
15378edd1043SGreg Roach	private function spStartHandler() {
1538a6f13a4aSGreg Roach		if ($this->print_data && $this->process_gedcoms === 0) {
1539a6f13a4aSGreg Roach			$this->current_element->addText(' ');
1540a6f13a4aSGreg Roach		}
1541a6f13a4aSGreg Roach	}
1542a6f13a4aSGreg Roach
1543a6f13a4aSGreg Roach	/**
154476692c8bSGreg Roach	 * XML <HighlightedImage/>
154576692c8bSGreg Roach	 *
1546a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1547a6f13a4aSGreg Roach	 */
15488edd1043SGreg Roach	private function highlightedImageStartHandler($attrs) {
1549e8e7866bSGreg Roach		global $WT_TREE;
1550a6f13a4aSGreg Roach
1551a6f13a4aSGreg Roach		$id    = '';
1552a6f13a4aSGreg Roach		$match = array();
1553a6f13a4aSGreg Roach		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1554a6f13a4aSGreg Roach			$id = $match[1];
1555a6f13a4aSGreg Roach		}
1556a6f13a4aSGreg Roach
1557a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
1558a6f13a4aSGreg Roach		$top = '.';
1559a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
1560a6f13a4aSGreg Roach			if ($attrs['top'] === '0') {
1561a6f13a4aSGreg Roach				$top = 0;
1562a6f13a4aSGreg Roach			} elseif ($attrs['top'] === '.') {
1563a6f13a4aSGreg Roach				$top = '.';
1564a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
1565a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
1566a6f13a4aSGreg Roach			}
1567a6f13a4aSGreg Roach		}
1568a6f13a4aSGreg Roach
1569a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. the default is the current position
1570a6f13a4aSGreg Roach		$left = '.';
1571a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
1572a6f13a4aSGreg Roach			if ($attrs['left'] === '0') {
1573a6f13a4aSGreg Roach				$left = 0;
1574a6f13a4aSGreg Roach			} elseif ($attrs['left'] === '.') {
1575a6f13a4aSGreg Roach				$left = '.';
1576a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
1577a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
1578a6f13a4aSGreg Roach			}
1579a6f13a4aSGreg Roach		}
1580a6f13a4aSGreg Roach
1581a6f13a4aSGreg Roach		// string Align the image in left, center, right
1582a6f13a4aSGreg Roach		$align = '';
1583a6f13a4aSGreg Roach		if (!empty($attrs['align'])) {
1584a6f13a4aSGreg Roach			$align = $attrs['align'];
1585a6f13a4aSGreg Roach		}
1586a6f13a4aSGreg Roach
1587a6f13a4aSGreg Roach		// string Next Line should be T:next to the image, N:next line
1588a6f13a4aSGreg Roach		$ln = '';
1589a6f13a4aSGreg Roach		if (!empty($attrs['ln'])) {
1590a6f13a4aSGreg Roach			$ln = $attrs['ln'];
1591a6f13a4aSGreg Roach		}
1592a6f13a4aSGreg Roach
1593a6f13a4aSGreg Roach		$width  = 0;
1594a6f13a4aSGreg Roach		$height = 0;
1595a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
1596a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
1597a6f13a4aSGreg Roach		}
1598a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
1599a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
1600a6f13a4aSGreg Roach		}
1601a6f13a4aSGreg Roach
1602a6f13a4aSGreg Roach		$person      = Individual::getInstance($id, $WT_TREE);
1603a6f13a4aSGreg Roach		$mediaobject = $person->findHighlightedMedia();
1604a6f13a4aSGreg Roach		if ($mediaobject) {
1605a6f13a4aSGreg Roach			$attributes = $mediaobject->getImageAttributes('thumb');
1606a6f13a4aSGreg Roach			if (in_array(
1607a6f13a4aSGreg Roach					$attributes['ext'],
1608a6f13a4aSGreg Roach					array(
1609a6f13a4aSGreg Roach						'GIF',
1610a6f13a4aSGreg Roach						'JPG',
1611a6f13a4aSGreg Roach						'PNG',
1612a6f13a4aSGreg Roach						'SWF',
1613a6f13a4aSGreg Roach						'PSD',
1614a6f13a4aSGreg Roach						'BMP',
1615a6f13a4aSGreg Roach						'TIFF',
1616a6f13a4aSGreg Roach						'TIFF',
1617a6f13a4aSGreg Roach						'JPC',
1618a6f13a4aSGreg Roach						'JP2',
1619a6f13a4aSGreg Roach						'JPX',
1620a6f13a4aSGreg Roach						'JB2',
1621a6f13a4aSGreg Roach						'SWC',
1622a6f13a4aSGreg Roach						'IFF',
1623a6f13a4aSGreg Roach						'WBMP',
1624a6f13a4aSGreg Roach						'XBM',
1625a6f13a4aSGreg Roach					)
1626a6f13a4aSGreg Roach				) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1627a6f13a4aSGreg Roach			) {
1628a6f13a4aSGreg Roach				if ($width > 0 && $height == 0) {
1629a6f13a4aSGreg Roach					$perc   = $width / $attributes['adjW'];
1630a6f13a4aSGreg Roach					$height = round($attributes['adjH'] * $perc);
1631a6f13a4aSGreg Roach				} elseif ($height > 0 && $width == 0) {
1632a6f13a4aSGreg Roach					$perc  = $height / $attributes['adjH'];
1633a6f13a4aSGreg Roach					$width = round($attributes['adjW'] * $perc);
1634a6f13a4aSGreg Roach				} else {
1635a6f13a4aSGreg Roach					$width  = $attributes['adjW'];
1636a6f13a4aSGreg Roach					$height = $attributes['adjH'];
1637a6f13a4aSGreg Roach				}
1638e8e7866bSGreg Roach				$image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1639e8e7866bSGreg Roach				$this->wt_report->addElement($image);
1640a6f13a4aSGreg Roach			}
1641a6f13a4aSGreg Roach		}
1642a6f13a4aSGreg Roach	}
1643a6f13a4aSGreg Roach
1644a6f13a4aSGreg Roach	/**
164576692c8bSGreg Roach	 * XML <Image/>
164676692c8bSGreg Roach	 *
1647a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1648a6f13a4aSGreg Roach	 */
16498edd1043SGreg Roach	private function imageStartHandler($attrs) {
1650e8e7866bSGreg Roach		global $WT_TREE;
1651a6f13a4aSGreg Roach
1652a6f13a4aSGreg Roach		// mixed Position the top corner of this box on the page. the default is the current position
1653a6f13a4aSGreg Roach		$top = '.';
1654a6f13a4aSGreg Roach		if (isset($attrs['top'])) {
1655a6f13a4aSGreg Roach			if ($attrs['top'] === "0") {
1656a6f13a4aSGreg Roach				$top = 0;
1657a6f13a4aSGreg Roach			} elseif ($attrs['top'] === '.') {
1658a6f13a4aSGreg Roach				$top = '.';
1659a6f13a4aSGreg Roach			} elseif (!empty($attrs['top'])) {
1660a6f13a4aSGreg Roach				$top = (int) $attrs['top'];
1661a6f13a4aSGreg Roach			}
1662a6f13a4aSGreg Roach		}
1663a6f13a4aSGreg Roach
1664a6f13a4aSGreg Roach		// mixed Position the left corner of this box on the page. the default is the current position
1665a6f13a4aSGreg Roach		$left = '.';
1666a6f13a4aSGreg Roach		if (isset($attrs['left'])) {
1667a6f13a4aSGreg Roach			if ($attrs['left'] === '0') {
1668a6f13a4aSGreg Roach				$left = 0;
1669a6f13a4aSGreg Roach			} elseif ($attrs['left'] === '.') {
1670a6f13a4aSGreg Roach				$left = '.';
1671a6f13a4aSGreg Roach			} elseif (!empty($attrs['left'])) {
1672a6f13a4aSGreg Roach				$left = (int) $attrs['left'];
1673a6f13a4aSGreg Roach			}
1674a6f13a4aSGreg Roach		}
1675a6f13a4aSGreg Roach
1676a6f13a4aSGreg Roach		// string Align the image in left, center, right
1677a6f13a4aSGreg Roach		$align = '';
1678a6f13a4aSGreg Roach		if (!empty($attrs['align'])) {
1679a6f13a4aSGreg Roach			$align = $attrs['align'];
1680a6f13a4aSGreg Roach		}
1681a6f13a4aSGreg Roach
1682a6f13a4aSGreg Roach		// string Next Line should be T:next to the image, N:next line
1683a6f13a4aSGreg Roach		$ln = 'T';
1684a6f13a4aSGreg Roach		if (!empty($attrs['ln'])) {
1685a6f13a4aSGreg Roach			$ln = $attrs['ln'];
1686a6f13a4aSGreg Roach		}
1687a6f13a4aSGreg Roach
1688a6f13a4aSGreg Roach		$width  = 0;
1689a6f13a4aSGreg Roach		$height = 0;
1690a6f13a4aSGreg Roach		if (!empty($attrs['width'])) {
1691a6f13a4aSGreg Roach			$width = (int) $attrs['width'];
1692a6f13a4aSGreg Roach		}
1693a6f13a4aSGreg Roach		if (!empty($attrs['height'])) {
1694a6f13a4aSGreg Roach			$height = (int) $attrs['height'];
1695a6f13a4aSGreg Roach		}
1696a6f13a4aSGreg Roach
1697a6f13a4aSGreg Roach		$file = '';
1698a6f13a4aSGreg Roach		if (!empty($attrs['file'])) {
1699a6f13a4aSGreg Roach			$file = $attrs['file'];
1700a6f13a4aSGreg Roach		}
1701a6f13a4aSGreg Roach		if ($file == "@FILE") {
1702a6f13a4aSGreg Roach			$match = array();
1703a6f13a4aSGreg Roach			if (preg_match("/\d OBJE @(.+)@/", $this->gedrec, $match)) {
1704a6f13a4aSGreg Roach				$mediaobject = Media::getInstance($match[1], $WT_TREE);
1705a6f13a4aSGreg Roach				$attributes  = $mediaobject->getImageAttributes('thumb');
1706a6f13a4aSGreg Roach				if (in_array(
1707a6f13a4aSGreg Roach						$attributes['ext'],
1708a6f13a4aSGreg Roach						array(
1709a6f13a4aSGreg Roach							'GIF',
1710a6f13a4aSGreg Roach							'JPG',
1711a6f13a4aSGreg Roach							'PNG',
1712a6f13a4aSGreg Roach							'SWF',
1713a6f13a4aSGreg Roach							'PSD',
1714a6f13a4aSGreg Roach							'BMP',
1715a6f13a4aSGreg Roach							'TIFF',
1716a6f13a4aSGreg Roach							'TIFF',
1717a6f13a4aSGreg Roach							'JPC',
1718a6f13a4aSGreg Roach							'JP2',
1719a6f13a4aSGreg Roach							'JPX',
1720a6f13a4aSGreg Roach							'JB2',
1721a6f13a4aSGreg Roach							'SWC',
1722a6f13a4aSGreg Roach							'IFF',
1723a6f13a4aSGreg Roach							'WBMP',
1724a6f13a4aSGreg Roach							'XBM',
1725a6f13a4aSGreg Roach						)
1726a6f13a4aSGreg Roach					) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1727a6f13a4aSGreg Roach				) {
1728a6f13a4aSGreg Roach					if ($width > 0 && $height == 0) {
1729a6f13a4aSGreg Roach						$perc   = $width / $attributes['adjW'];
1730a6f13a4aSGreg Roach						$height = round($attributes['adjH'] * $perc);
1731a6f13a4aSGreg Roach					} elseif ($height > 0 && $width == 0) {
1732a6f13a4aSGreg Roach						$perc  = $height / $attributes['adjH'];
1733a6f13a4aSGreg Roach						$width = round($attributes['adjW'] * $perc);
1734a6f13a4aSGreg Roach					} else {
1735a6f13a4aSGreg Roach						$width  = $attributes['adjW'];
1736a6f13a4aSGreg Roach						$height = $attributes['adjH'];
1737a6f13a4aSGreg Roach					}
1738e8e7866bSGreg Roach					$image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1739e8e7866bSGreg Roach					$this->wt_report->addElement($image);
1740a6f13a4aSGreg Roach				}
1741a6f13a4aSGreg Roach			}
1742a6f13a4aSGreg Roach		} else {
1743a6f13a4aSGreg Roach			if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
1744a6f13a4aSGreg Roach				$size = getimagesize($file);
1745a6f13a4aSGreg Roach				if ($width > 0 && $height == 0) {
1746a6f13a4aSGreg Roach					$perc   = $width / $size[0];
1747a6f13a4aSGreg Roach					$height = round($size[1] * $perc);
1748a6f13a4aSGreg Roach				} elseif ($height > 0 && $width == 0) {
1749a6f13a4aSGreg Roach					$perc  = $height / $size[1];
1750a6f13a4aSGreg Roach					$width = round($size[0] * $perc);
1751a6f13a4aSGreg Roach				} else {
1752a6f13a4aSGreg Roach					$width  = $size[0];
1753a6f13a4aSGreg Roach					$height = $size[1];
1754a6f13a4aSGreg Roach				}
1755e8e7866bSGreg Roach				$image = $this->report_root->createImage($file, $left, $top, $width, $height, $align, $ln);
1756e8e7866bSGreg Roach				$this->wt_report->addElement($image);
1757a6f13a4aSGreg Roach			}
1758a6f13a4aSGreg Roach		}
1759a6f13a4aSGreg Roach	}
1760a6f13a4aSGreg Roach
1761a6f13a4aSGreg Roach	/**
1762a6f13a4aSGreg Roach	 * XML <Line> element handler
1763a6f13a4aSGreg Roach	 *
1764a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1765a6f13a4aSGreg Roach	 */
17668edd1043SGreg Roach	private function lineStartHandler($attrs) {
1767a6f13a4aSGreg Roach		// Start horizontal position, current position (default)
1768a6f13a4aSGreg Roach		$x1 = ".";
1769a6f13a4aSGreg Roach		if (isset($attrs['x1'])) {
1770a6f13a4aSGreg Roach			if ($attrs['x1'] === "0") {
1771a6f13a4aSGreg Roach				$x1 = 0;
1772a6f13a4aSGreg Roach			} elseif ($attrs['x1'] === ".") {
1773a6f13a4aSGreg Roach				$x1 = ".";
1774a6f13a4aSGreg Roach			} elseif (!empty($attrs['x1'])) {
1775a6f13a4aSGreg Roach				$x1 = (int) $attrs['x1'];
1776a6f13a4aSGreg Roach			}
1777a6f13a4aSGreg Roach		}
1778a6f13a4aSGreg Roach		// Start vertical position, current position (default)
1779a6f13a4aSGreg Roach		$y1 = ".";
1780a6f13a4aSGreg Roach		if (isset($attrs['y1'])) {
1781a6f13a4aSGreg Roach			if ($attrs['y1'] === "0") {
1782a6f13a4aSGreg Roach				$y1 = 0;
1783a6f13a4aSGreg Roach			} elseif ($attrs['y1'] === ".") {
1784a6f13a4aSGreg Roach				$y1 = ".";
1785a6f13a4aSGreg Roach			} elseif (!empty($attrs['y1'])) {
1786a6f13a4aSGreg Roach				$y1 = (int) $attrs['y1'];
1787a6f13a4aSGreg Roach			}
1788a6f13a4aSGreg Roach		}
1789a6f13a4aSGreg Roach		// End horizontal position, maximum width (default)
1790a6f13a4aSGreg Roach		$x2 = ".";
1791a6f13a4aSGreg Roach		if (isset($attrs['x2'])) {
1792a6f13a4aSGreg Roach			if ($attrs['x2'] === "0") {
1793a6f13a4aSGreg Roach				$x2 = 0;
1794a6f13a4aSGreg Roach			} elseif ($attrs['x2'] === ".") {
1795a6f13a4aSGreg Roach				$x2 = ".";
1796a6f13a4aSGreg Roach			} elseif (!empty($attrs['x2'])) {
1797a6f13a4aSGreg Roach				$x2 = (int) $attrs['x2'];
1798a6f13a4aSGreg Roach			}
1799a6f13a4aSGreg Roach		}
1800a6f13a4aSGreg Roach		// End vertical position
1801a6f13a4aSGreg Roach		$y2 = ".";
1802a6f13a4aSGreg Roach		if (isset($attrs['y2'])) {
1803a6f13a4aSGreg Roach			if ($attrs['y2'] === "0") {
1804a6f13a4aSGreg Roach				$y2 = 0;
1805a6f13a4aSGreg Roach			} elseif ($attrs['y2'] === ".") {
1806a6f13a4aSGreg Roach				$y2 = ".";
1807a6f13a4aSGreg Roach			} elseif (!empty($attrs['y2'])) {
1808a6f13a4aSGreg Roach				$y2 = (int) $attrs['y2'];
1809a6f13a4aSGreg Roach			}
1810a6f13a4aSGreg Roach		}
1811a6f13a4aSGreg Roach
1812e8e7866bSGreg Roach		$line = $this->report_root->createLine($x1, $y1, $x2, $y2);
1813e8e7866bSGreg Roach		$this->wt_report->addElement($line);
1814a6f13a4aSGreg Roach	}
1815a6f13a4aSGreg Roach
1816a6f13a4aSGreg Roach	/**
181776692c8bSGreg Roach	 * XML <List>
1818a6f13a4aSGreg Roach	 *
1819a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
1820a6f13a4aSGreg Roach	 */
18218edd1043SGreg Roach	private function listStartHandler($attrs) {
1822d1286247SGreg Roach		global $WT_TREE;
1823a6f13a4aSGreg Roach
1824a6f13a4aSGreg Roach		$this->process_repeats++;
1825a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
1826a6f13a4aSGreg Roach			return;
1827a6f13a4aSGreg Roach		}
1828a6f13a4aSGreg Roach
1829a6f13a4aSGreg Roach		$match = array();
1830a6f13a4aSGreg Roach		if (isset($attrs['sortby'])) {
1831a6f13a4aSGreg Roach			$sortby = $attrs['sortby'];
1832a6f13a4aSGreg Roach			if (preg_match("/\\$(\w+)/", $sortby, $match)) {
1833d1286247SGreg Roach				$sortby = $this->vars[$match[1]]['id'];
1834a6f13a4aSGreg Roach				$sortby = trim($sortby);
1835a6f13a4aSGreg Roach			}
1836a6f13a4aSGreg Roach		} else {
1837a6f13a4aSGreg Roach			$sortby = "NAME";
1838a6f13a4aSGreg Roach		}
1839a6f13a4aSGreg Roach
1840a6f13a4aSGreg Roach		if (isset($attrs['list'])) {
1841a6f13a4aSGreg Roach			$listname = $attrs['list'];
1842a6f13a4aSGreg Roach		} else {
1843a6f13a4aSGreg Roach			$listname = "individual";
1844a6f13a4aSGreg Roach		}
1845a6f13a4aSGreg Roach		// Some filters/sorts can be applied using SQL, while others require PHP
1846a6f13a4aSGreg Roach		switch ($listname) {
1847a6f13a4aSGreg Roach			case "pending":
1848a6f13a4aSGreg Roach				$rows = Database::prepare(
18495d0bc43dSGreg Roach					"SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" .
18505d0bc43dSGreg Roach					" FROM `##change`" . " WHERE (xref, change_id) IN (" .
18515d0bc43dSGreg Roach					"  SELECT xref, MAX(change_id)" .
18525d0bc43dSGreg Roach					"  FROM `##change`" .
18535d0bc43dSGreg Roach					"  WHERE status = 'pending' AND gedcom_id = :tree_id" .
18545d0bc43dSGreg Roach					"  GROUP BY xref" .
18555d0bc43dSGreg Roach					" )"
18565d0bc43dSGreg Roach				)->execute(array(
185776692c8bSGreg Roach					'tree_id' => $WT_TREE->getTreeId(),
18585d0bc43dSGreg Roach				))->fetchAll();
1859a6f13a4aSGreg Roach				$this->list = array();
1860a6f13a4aSGreg Roach				foreach ($rows as $row) {
1861a6f13a4aSGreg Roach					$this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1862a6f13a4aSGreg Roach				}
1863a6f13a4aSGreg Roach				break;
1864a6f13a4aSGreg Roach			case 'individual':
1865a6f13a4aSGreg Roach				$sql_select   = "SELECT DISTINCT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1866a6f13a4aSGreg Roach				$sql_join     = "";
1867825006d2SGreg Roach				$sql_where    = " WHERE i_file = :tree_id";
1868a6f13a4aSGreg Roach				$sql_order_by = "";
1869825006d2SGreg Roach				$sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1870a6f13a4aSGreg Roach				foreach ($attrs as $attr => $value) {
1871a6f13a4aSGreg Roach					if (strpos($attr, 'filter') === 0 && $value) {
187282759250SGreg Roach						$value = $this->substituteVars($value, false);
1873a6f13a4aSGreg Roach						// Convert the various filters into SQL
1874a6f13a4aSGreg Roach						if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1875a6f13a4aSGreg Roach							$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1876*b0d2e743SGreg Roach							$sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
18775d0bc43dSGreg Roach							$sql_params[$attr . 'fact'] = $match[1];
1878a6f13a4aSGreg Roach							$date                       = new Date($match[3]);
1879a6f13a4aSGreg Roach							if ($match[2] == "LTE") {
18805d0bc43dSGreg Roach								$sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
18815d0bc43dSGreg Roach								$sql_params[$attr . 'date'] = $date->maximumJulianDay();
1882a6f13a4aSGreg Roach							} else {
18835d0bc43dSGreg Roach								$sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
18845d0bc43dSGreg Roach								$sql_params[$attr . 'date'] = $date->minimumJulianDay();
1885a6f13a4aSGreg Roach							}
1886a6f13a4aSGreg Roach							if ($sortby == $match[1]) {
1887a6f13a4aSGreg Roach								$sortby = "";
1888a6f13a4aSGreg Roach								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1889a6f13a4aSGreg Roach							}
1890a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1891a6f13a4aSGreg Roach						} elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1892a6f13a4aSGreg Roach							// Do nothing, unless you have to
1893a6f13a4aSGreg Roach							if ($match[1] != '' || $sortby == 'NAME') {
1894a6f13a4aSGreg Roach								$sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1895a6f13a4aSGreg Roach								// Search the DB only if there is any name supplied
1896a6f13a4aSGreg Roach								if ($match[1] != "") {
1897a6f13a4aSGreg Roach									$names = explode(" ", $match[1]);
18985d0bc43dSGreg Roach									foreach ($names as $n => $name) {
18995d0bc43dSGreg Roach										$sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
19005d0bc43dSGreg Roach										$sql_params[$attr . 'name' . $n] = $name;
1901a6f13a4aSGreg Roach									}
1902a6f13a4aSGreg Roach								}
1903a6f13a4aSGreg Roach								// Let the DB do the name sorting even when no name was entered
1904a6f13a4aSGreg Roach								if ($sortby == "NAME") {
1905a6f13a4aSGreg Roach									$sortby = "";
1906a6f13a4aSGreg Roach									$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1907a6f13a4aSGreg Roach								}
1908a6f13a4aSGreg Roach							}
1909a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1910a6f13a4aSGreg Roach						} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
19115d0bc43dSGreg Roach							$sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
19125d0bc43dSGreg Roach							$sql_params[$attr . 'gedcom'] = $match[1];
1913a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1914a6f13a4aSGreg Roach						} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1915a6f13a4aSGreg Roach							$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file = i_file)";
1916a6f13a4aSGreg 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)";
19175d0bc43dSGreg Roach							$sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
19185d0bc43dSGreg Roach							$sql_params[$attr . 'place'] = $match[1];
1919a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
1920a6f13a4aSGreg Roach						} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
19215d0bc43dSGreg Roach							$sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
19225d0bc43dSGreg Roach							$sql_params[$attr . 'contains1'] = $match[1];
19235d0bc43dSGreg Roach							$sql_params[$attr . 'contains2'] = $match[2];
19245d0bc43dSGreg Roach							$sql_params[$attr . 'contains3'] = $match[3];
1925a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
1926a6f13a4aSGreg Roach						}
1927a6f13a4aSGreg Roach					}
1928a6f13a4aSGreg Roach				}
1929a6f13a4aSGreg Roach
1930a6f13a4aSGreg Roach				$this->list = array();
1931a6f13a4aSGreg Roach				$rows       = Database::prepare(
1932a6f13a4aSGreg Roach					$sql_select . $sql_join . $sql_where . $sql_order_by
19335d0bc43dSGreg Roach				)->execute($sql_params)->fetchAll();
1934a6f13a4aSGreg Roach
1935a6f13a4aSGreg Roach				foreach ($rows as $row) {
1936a6f13a4aSGreg Roach					$this->list[] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1937a6f13a4aSGreg Roach				}
1938a6f13a4aSGreg Roach				break;
1939a6f13a4aSGreg Roach
1940a6f13a4aSGreg Roach			case 'family':
1941a6f13a4aSGreg Roach				$sql_select   = "SELECT DISTINCT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
1942a6f13a4aSGreg Roach				$sql_join     = "";
1943825006d2SGreg Roach				$sql_where    = " WHERE f_file = :tree_id";
1944a6f13a4aSGreg Roach				$sql_order_by = "";
1945825006d2SGreg Roach				$sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1946a6f13a4aSGreg Roach				foreach ($attrs as $attr => $value) {
1947a6f13a4aSGreg Roach					if (strpos($attr, 'filter') === 0 && $value) {
194882759250SGreg Roach						$value = $this->substituteVars($value, false);
1949a6f13a4aSGreg Roach						// Convert the various filters into SQL
1950a6f13a4aSGreg Roach						if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1951*b0d2e743SGreg Roach							$sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
19525d0bc43dSGreg Roach							$sql_params[$attr . 'fact'] = $match[1];
1953a6f13a4aSGreg Roach							$date                       = new Date($match[3]);
1954a6f13a4aSGreg Roach							if ($match[2] == "LTE") {
19555d0bc43dSGreg Roach								$sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
19565d0bc43dSGreg Roach								$sql_params[$attr . 'date'] = $date->maximumJulianDay();
1957a6f13a4aSGreg Roach							} else {
19585d0bc43dSGreg Roach								$sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
19595d0bc43dSGreg Roach								$sql_params[$attr . 'date'] = $date->minimumJulianDay();
1960a6f13a4aSGreg Roach							}
1961a6f13a4aSGreg Roach							if ($sortby == $match[1]) {
1962a6f13a4aSGreg Roach								$sortby = "";
1963a6f13a4aSGreg Roach								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1964a6f13a4aSGreg Roach							}
1965a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1966a6f13a4aSGreg Roach						} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
19675d0bc43dSGreg Roach							$sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
19685d0bc43dSGreg Roach							$sql_params[$attr . 'gedcom'] = $match[1];
1969a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
1970a6f13a4aSGreg Roach						} elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
19715d0bc43dSGreg Roach							// Do nothing, unless you have to
19725d0bc43dSGreg Roach							if ($match[1] != '' || $sortby == 'NAME') {
19735d0bc43dSGreg Roach								$sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
19745d0bc43dSGreg Roach								// Search the DB only if there is any name supplied
19755d0bc43dSGreg Roach								if ($match[1] != "") {
19765d0bc43dSGreg Roach									$names = explode(" ", $match[1]);
19775d0bc43dSGreg Roach									foreach ($names as $n => $name) {
19785d0bc43dSGreg Roach										$sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
19795d0bc43dSGreg Roach										$sql_params[$attr . 'name' . $n] = $name;
19805d0bc43dSGreg Roach									}
19815d0bc43dSGreg Roach								}
19825d0bc43dSGreg Roach								// Let the DB do the name sorting even when no name was entered
1983a6f13a4aSGreg Roach								if ($sortby == "NAME") {
1984a6f13a4aSGreg Roach									$sortby = "";
1985a6f13a4aSGreg Roach									$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1986a6f13a4aSGreg Roach								}
19875d0bc43dSGreg Roach							}
1988a6f13a4aSGreg Roach							unset($attrs[$attr]); // This filter has been fully processed
19895d0bc43dSGreg Roach
1990a6f13a4aSGreg Roach						} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1991a6f13a4aSGreg Roach							$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
1992a6f13a4aSGreg 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)";
19935d0bc43dSGreg Roach							$sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
19945d0bc43dSGreg Roach							$sql_params[$attr . 'place'] = $match[1];
1995a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
1996a6f13a4aSGreg Roach						} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
19975d0bc43dSGreg Roach							$sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
19985d0bc43dSGreg Roach							$sql_params[$attr . 'contains1'] = $match[1];
19995d0bc43dSGreg Roach							$sql_params[$attr . 'contains2'] = $match[2];
20005d0bc43dSGreg Roach							$sql_params[$attr . 'contains3'] = $match[3];
2001a6f13a4aSGreg Roach							// Don't unset this filter. This is just initial filtering
2002a6f13a4aSGreg Roach						}
2003a6f13a4aSGreg Roach					}
2004a6f13a4aSGreg Roach				}
2005a6f13a4aSGreg Roach
2006a6f13a4aSGreg Roach				$this->list = array();
2007a6f13a4aSGreg Roach				$rows       = Database::prepare(
2008a6f13a4aSGreg Roach					$sql_select . $sql_join . $sql_where . $sql_order_by
20095d0bc43dSGreg Roach				)->execute($sql_params)->fetchAll();
2010a6f13a4aSGreg Roach
2011a6f13a4aSGreg Roach				foreach ($rows as $row) {
2012a6f13a4aSGreg Roach					$this->list[] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2013a6f13a4aSGreg Roach				}
2014a6f13a4aSGreg Roach				break;
2015a6f13a4aSGreg Roach
2016a6f13a4aSGreg Roach			default:
2017a6f13a4aSGreg Roach				throw new \DomainException('Invalid list name: ' . $listname);
2018a6f13a4aSGreg Roach		}
2019a6f13a4aSGreg Roach
2020a6f13a4aSGreg Roach		$filters  = array();
2021a6f13a4aSGreg Roach		$filters2 = array();
2022a6f13a4aSGreg Roach		if (isset($attrs['filter1']) && count($this->list) > 0) {
2023a6f13a4aSGreg Roach			foreach ($attrs as $key => $value) {
2024a6f13a4aSGreg Roach				if (preg_match("/filter(\d)/", $key)) {
2025a6f13a4aSGreg Roach					$condition = $value;
2026a6f13a4aSGreg Roach					if (preg_match("/@(\w+)/", $condition, $match)) {
2027a6f13a4aSGreg Roach						$id    = $match[1];
2028a6f13a4aSGreg Roach						$value = "''";
2029a6f13a4aSGreg Roach						if ($id == "ID") {
2030a6f13a4aSGreg Roach							if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
2031a6f13a4aSGreg Roach								$value = "'" . $match[1] . "'";
2032a6f13a4aSGreg Roach							}
2033a6f13a4aSGreg Roach						} elseif ($id == "fact") {
2034a6f13a4aSGreg Roach							$value = "'" . $this->fact . "'";
2035a6f13a4aSGreg Roach						} elseif ($id == "desc") {
2036a6f13a4aSGreg Roach							$value = "'" . $this->desc . "'";
2037a6f13a4aSGreg Roach						} else {
2038a6f13a4aSGreg Roach							if (preg_match("/\d $id (.+)/", $this->gedrec, $match)) {
2039a6f13a4aSGreg Roach								$value = "'" . str_replace("@", "", trim($match[1])) . "'";
2040a6f13a4aSGreg Roach							}
2041a6f13a4aSGreg Roach						}
2042a6f13a4aSGreg Roach						$condition = preg_replace("/@$id/", $value, $condition);
2043a6f13a4aSGreg Roach					}
2044a6f13a4aSGreg Roach					//-- handle regular expressions
2045a6f13a4aSGreg Roach					if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
2046a6f13a4aSGreg Roach						$tag  = trim($match[1]);
2047a6f13a4aSGreg Roach						$expr = trim($match[2]);
2048a6f13a4aSGreg Roach						$val  = trim($match[3]);
2049a6f13a4aSGreg Roach						if (preg_match("/\\$(\w+)/", $val, $match)) {
2050d1286247SGreg Roach							$val = $this->vars[$match[1]]['id'];
2051a6f13a4aSGreg Roach							$val = trim($val);
2052a6f13a4aSGreg Roach						}
2053a6f13a4aSGreg Roach						if ($val) {
2054a6f13a4aSGreg Roach							$searchstr = "";
2055a6f13a4aSGreg Roach							$tags      = explode(":", $tag);
2056a6f13a4aSGreg Roach							//-- only limit to a level number if we are specifically looking at a level
2057a6f13a4aSGreg Roach							if (count($tags) > 1) {
2058a6f13a4aSGreg Roach								$level = 1;
2059a6f13a4aSGreg Roach								foreach ($tags as $t) {
2060a6f13a4aSGreg Roach									if (!empty($searchstr)) {
2061a6f13a4aSGreg Roach										$searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
2062a6f13a4aSGreg Roach									}
2063a6f13a4aSGreg Roach									//-- search for both EMAIL and _EMAIL... silly double gedcom standard
2064a6f13a4aSGreg Roach									if ($t == "EMAIL" || $t == "_EMAIL") {
2065a6f13a4aSGreg Roach										$t = "_?EMAIL";
2066a6f13a4aSGreg Roach									}
2067a6f13a4aSGreg Roach									$searchstr .= $level . " " . $t;
2068a6f13a4aSGreg Roach									$level++;
2069a6f13a4aSGreg Roach								}
2070a6f13a4aSGreg Roach							} else {
2071a6f13a4aSGreg Roach								if ($tag == "EMAIL" || $tag == "_EMAIL") {
2072a6f13a4aSGreg Roach									$tag = "_?EMAIL";
2073a6f13a4aSGreg Roach								}
2074a6f13a4aSGreg Roach								$t         = $tag;
2075a6f13a4aSGreg Roach								$searchstr = "1 " . $tag;
2076a6f13a4aSGreg Roach							}
2077a6f13a4aSGreg Roach							switch ($expr) {
2078a6f13a4aSGreg Roach								case "CONTAINS":
2079a6f13a4aSGreg Roach									if ($t == "PLAC") {
2080a6f13a4aSGreg Roach										$searchstr .= "[^\n]*[, ]*" . $val;
2081a6f13a4aSGreg Roach									} else {
2082a6f13a4aSGreg Roach										$searchstr .= "[^\n]*" . $val;
2083a6f13a4aSGreg Roach									}
2084a6f13a4aSGreg Roach									$filters[] = $searchstr;
2085a6f13a4aSGreg Roach									break;
2086a6f13a4aSGreg Roach								default:
2087a6f13a4aSGreg Roach									$filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2088a6f13a4aSGreg Roach									break;
2089a6f13a4aSGreg Roach							}
2090a6f13a4aSGreg Roach						}
2091a6f13a4aSGreg Roach					}
2092a6f13a4aSGreg Roach				}
2093a6f13a4aSGreg Roach			}
2094a6f13a4aSGreg Roach		}
2095a6f13a4aSGreg Roach		//-- apply other filters to the list that could not be added to the search string
2096a6f13a4aSGreg Roach		if ($filters) {
2097a6f13a4aSGreg Roach			foreach ($this->list as $key => $record) {
2098a6f13a4aSGreg Roach				foreach ($filters as $filter) {
2099a6f13a4aSGreg Roach					if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
2100a6f13a4aSGreg Roach						unset($this->list[$key]);
2101a6f13a4aSGreg Roach						break;
2102a6f13a4aSGreg Roach					}
2103a6f13a4aSGreg Roach				}
2104a6f13a4aSGreg Roach			}
2105a6f13a4aSGreg Roach		}
2106a6f13a4aSGreg Roach		if ($filters2) {
2107a6f13a4aSGreg Roach			$mylist = array();
2108a6f13a4aSGreg Roach			foreach ($this->list as $indi) {
2109a6f13a4aSGreg Roach				$key  = $indi->getXref();
2110a6f13a4aSGreg Roach				$grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
2111a6f13a4aSGreg Roach				$keep = true;
2112a6f13a4aSGreg Roach				foreach ($filters2 as $filter) {
2113a6f13a4aSGreg Roach					if ($keep) {
2114a6f13a4aSGreg Roach						$tag  = $filter['tag'];
2115a6f13a4aSGreg Roach						$expr = $filter['expr'];
2116a6f13a4aSGreg Roach						$val  = $filter['val'];
2117a6f13a4aSGreg Roach						if ($val == "''") {
2118a6f13a4aSGreg Roach							$val = "";
2119a6f13a4aSGreg Roach						}
2120a6f13a4aSGreg Roach						$tags = explode(":", $tag);
2121a6f13a4aSGreg Roach						$t    = end($tags);
21223d7a8a4cSGreg Roach						$v    = $this->getGedcomValue($tag, 1, $grec);
2123a6f13a4aSGreg Roach						//-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
2124a6f13a4aSGreg Roach						if ($t == "EMAIL" && empty($v)) {
2125a6f13a4aSGreg Roach							$tag  = str_replace("EMAIL", "_EMAIL", $tag);
2126a6f13a4aSGreg Roach							$tags = explode(":", $tag);
2127a6f13a4aSGreg Roach							$t    = end($tags);
21283d7a8a4cSGreg Roach							$v    = Functions::getSubRecord(1, $tag, $grec);
2129a6f13a4aSGreg Roach						}
2130a6f13a4aSGreg Roach
2131a6f13a4aSGreg Roach						switch ($expr) {
2132a6f13a4aSGreg Roach							case "GTE":
2133a6f13a4aSGreg Roach								if ($t == "DATE") {
2134a6f13a4aSGreg Roach									$date1 = new Date($v);
2135a6f13a4aSGreg Roach									$date2 = new Date($val);
2136a6f13a4aSGreg Roach									$keep  = (Date::compare($date1, $date2) >= 0);
2137a6f13a4aSGreg Roach								} elseif ($val >= $v) {
2138a6f13a4aSGreg Roach									$keep = true;
2139a6f13a4aSGreg Roach								}
2140a6f13a4aSGreg Roach								break;
2141a6f13a4aSGreg Roach							case "LTE":
2142a6f13a4aSGreg Roach								if ($t == "DATE") {
2143a6f13a4aSGreg Roach									$date1 = new Date($v);
2144a6f13a4aSGreg Roach									$date2 = new Date($val);
2145a6f13a4aSGreg Roach									$keep  = (Date::compare($date1, $date2) <= 0);
2146a6f13a4aSGreg Roach								} elseif ($val >= $v) {
2147a6f13a4aSGreg Roach									$keep = true;
2148a6f13a4aSGreg Roach								}
2149a6f13a4aSGreg Roach								break;
2150a6f13a4aSGreg Roach							default:
2151a6f13a4aSGreg Roach								if ($v == $val) {
2152a6f13a4aSGreg Roach									$keep = true;
2153a6f13a4aSGreg Roach								} else {
2154a6f13a4aSGreg Roach									$keep = false;
2155a6f13a4aSGreg Roach								}
2156a6f13a4aSGreg Roach								break;
2157a6f13a4aSGreg Roach						}
2158a6f13a4aSGreg Roach					}
2159a6f13a4aSGreg Roach				}
2160a6f13a4aSGreg Roach				if ($keep) {
2161a6f13a4aSGreg Roach					$mylist[$key] = $indi;
2162a6f13a4aSGreg Roach				}
2163a6f13a4aSGreg Roach			}
2164a6f13a4aSGreg Roach			$this->list = $mylist;
2165a6f13a4aSGreg Roach		}
2166a6f13a4aSGreg Roach
2167a6f13a4aSGreg Roach		switch ($sortby) {
2168a6f13a4aSGreg Roach			case 'NAME':
2169a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2170a6f13a4aSGreg Roach				break;
2171a6f13a4aSGreg Roach			case 'CHAN':
2172a6f13a4aSGreg Roach				uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2173a6f13a4aSGreg Roach					return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2174a6f13a4aSGreg Roach				});
2175a6f13a4aSGreg Roach				break;
2176a6f13a4aSGreg Roach			case 'BIRT:DATE':
2177a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2178a6f13a4aSGreg Roach				break;
2179a6f13a4aSGreg Roach			case 'DEAT:DATE':
2180a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2181a6f13a4aSGreg Roach				break;
2182a6f13a4aSGreg Roach			case 'MARR:DATE':
21835d0bc43dSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Family::compareMarrDate');
2184a6f13a4aSGreg Roach				break;
2185a6f13a4aSGreg Roach			default:
2186a6f13a4aSGreg Roach				// unsorted or already sorted by SQL
2187a6f13a4aSGreg Roach				break;
2188a6f13a4aSGreg Roach		}
2189a6f13a4aSGreg Roach
2190a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2191e8e7866bSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2192a6f13a4aSGreg Roach	}
2193a6f13a4aSGreg Roach
2194a6f13a4aSGreg Roach	/**
219576692c8bSGreg Roach	 * XML <List>
2196a6f13a4aSGreg Roach	 */
21978edd1043SGreg Roach	private function listEndHandler() {
2198e8e7866bSGreg Roach		global $report;
2199a6f13a4aSGreg Roach
2200a6f13a4aSGreg Roach		$this->process_repeats--;
2201a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
2202a6f13a4aSGreg Roach			return;
2203a6f13a4aSGreg Roach		}
2204a6f13a4aSGreg Roach
2205a6f13a4aSGreg Roach		// Check if there is any list
2206a6f13a4aSGreg Roach		if (count($this->list) > 0) {
2207a6f13a4aSGreg Roach			$lineoffset = 0;
2208a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
2209a6f13a4aSGreg Roach				$lineoffset += $rep[1];
2210a6f13a4aSGreg Roach			}
2211a6f13a4aSGreg Roach			//-- read the xml from the file
2212a6f13a4aSGreg Roach			$lines = file($report);
2213a6f13a4aSGreg Roach			while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<List") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2214a6f13a4aSGreg Roach				$lineoffset--;
2215a6f13a4aSGreg Roach			}
2216a6f13a4aSGreg Roach			$lineoffset++;
2217a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
2218a6f13a4aSGreg Roach			$line_nr   = $lineoffset + $this->repeat_bytes;
2219a6f13a4aSGreg Roach			// List Level counter
2220a6f13a4aSGreg Roach			$count = 1;
2221a6f13a4aSGreg Roach			while (0 < $count) {
2222a6f13a4aSGreg Roach				if (strpos($lines[$line_nr], "<List") !== false) {
2223a6f13a4aSGreg Roach					$count++;
2224a6f13a4aSGreg Roach				} elseif (strpos($lines[$line_nr], "</List") !== false) {
2225a6f13a4aSGreg Roach					$count--;
2226a6f13a4aSGreg Roach				}
2227a6f13a4aSGreg Roach				if (0 < $count) {
2228a6f13a4aSGreg Roach					$reportxml .= $lines[$line_nr];
2229a6f13a4aSGreg Roach				}
2230a6f13a4aSGreg Roach				$line_nr++;
2231a6f13a4aSGreg Roach			}
2232a6f13a4aSGreg Roach			// No need to drag this
2233a6f13a4aSGreg Roach			unset($lines);
2234a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>";
2235a6f13a4aSGreg Roach			// Save original values
2236e8e7866bSGreg Roach			array_push($this->parser_stack, $this->parser);
2237a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
2238a6f13a4aSGreg Roach
2239a6f13a4aSGreg Roach			$this->list_total   = count($this->list);
2240a6f13a4aSGreg Roach			$this->list_private = 0;
2241a6f13a4aSGreg Roach			foreach ($this->list as $record) {
2242a6f13a4aSGreg Roach				if ($record->canShow()) {
2243a6f13a4aSGreg Roach					$this->gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
2244a6f13a4aSGreg Roach					//-- start the sax parser
2245a6f13a4aSGreg Roach					$repeat_parser = xml_parser_create();
2246e8e7866bSGreg Roach					$this->parser  = $repeat_parser;
2247a6f13a4aSGreg Roach					xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2248a6f13a4aSGreg Roach					xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2249a6f13a4aSGreg Roach					xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2250a6f13a4aSGreg Roach					if (!xml_parse($repeat_parser, $reportxml, true)) {
2251a6f13a4aSGreg Roach						throw new \DomainException(sprintf(
2252a6f13a4aSGreg Roach							'ListEHandler XML error: %s at line %d',
2253a6f13a4aSGreg Roach							xml_error_string(xml_get_error_code($repeat_parser)),
2254a6f13a4aSGreg Roach							xml_get_current_line_number($repeat_parser)
2255a6f13a4aSGreg Roach						));
2256a6f13a4aSGreg Roach					}
2257a6f13a4aSGreg Roach					xml_parser_free($repeat_parser);
2258a6f13a4aSGreg Roach				} else {
2259a6f13a4aSGreg Roach					$this->list_private++;
2260a6f13a4aSGreg Roach				}
2261a6f13a4aSGreg Roach			}
2262a6f13a4aSGreg Roach			$this->list   = array();
2263e8e7866bSGreg Roach			$this->parser = array_pop($this->parser_stack);
2264a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
2265a6f13a4aSGreg Roach		}
2266a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2267a6f13a4aSGreg Roach	}
2268a6f13a4aSGreg Roach
2269a6f13a4aSGreg Roach	/**
2270a6f13a4aSGreg Roach	 * XML <ListTotal> element handler
2271a6f13a4aSGreg Roach	 *
2272a6f13a4aSGreg Roach	 * Prints the total number of records in a list
2273a6f13a4aSGreg Roach	 * The total number is collected from
2274a6f13a4aSGreg Roach	 * List and Relatives
2275a6f13a4aSGreg Roach	 */
22768edd1043SGreg Roach	private function listTotalStartHandler() {
2277a6f13a4aSGreg Roach		if ($this->list_private == 0) {
2278a6f13a4aSGreg Roach			$this->current_element->addText($this->list_total);
2279a6f13a4aSGreg Roach		} else {
2280a6f13a4aSGreg Roach			$this->current_element->addText(($this->list_total - $this->list_private) . " / " . $this->list_total);
2281a6f13a4aSGreg Roach		}
2282a6f13a4aSGreg Roach	}
2283a6f13a4aSGreg Roach
2284a6f13a4aSGreg Roach	/**
228576692c8bSGreg Roach	 * XML <Relatives>
228676692c8bSGreg Roach	 *
2287a6f13a4aSGreg Roach	 * @param array $attrs an array of key value pairs for the attributes
2288a6f13a4aSGreg Roach	 */
22898edd1043SGreg Roach	private function relativesStartHandler($attrs) {
2290d1286247SGreg Roach		global $WT_TREE;
2291a6f13a4aSGreg Roach
2292a6f13a4aSGreg Roach		$this->process_repeats++;
2293a6f13a4aSGreg Roach		if ($this->process_repeats > 1) {
2294a6f13a4aSGreg Roach			return;
2295a6f13a4aSGreg Roach		}
2296a6f13a4aSGreg Roach
2297a6f13a4aSGreg Roach		$sortby = "NAME";
2298a6f13a4aSGreg Roach		if (isset($attrs['sortby'])) {
2299a6f13a4aSGreg Roach			$sortby = $attrs['sortby'];
2300a6f13a4aSGreg Roach		}
2301a6f13a4aSGreg Roach		$match = array();
2302a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $sortby, $match)) {
2303d1286247SGreg Roach			$sortby = $this->vars[$match[1]]['id'];
2304a6f13a4aSGreg Roach			$sortby = trim($sortby);
2305a6f13a4aSGreg Roach		}
2306a6f13a4aSGreg Roach
2307a6f13a4aSGreg Roach		$maxgen = -1;
2308a6f13a4aSGreg Roach		if (isset($attrs['maxgen'])) {
2309a6f13a4aSGreg Roach			$maxgen = $attrs['maxgen'];
2310a6f13a4aSGreg Roach		}
2311a6f13a4aSGreg Roach		if ($maxgen == "*") {
2312a6f13a4aSGreg Roach			$maxgen = -1;
2313a6f13a4aSGreg Roach		}
2314a6f13a4aSGreg Roach
2315a6f13a4aSGreg Roach		$group = "child-family";
2316a6f13a4aSGreg Roach		if (isset($attrs['group'])) {
2317a6f13a4aSGreg Roach			$group = $attrs['group'];
2318a6f13a4aSGreg Roach		}
2319a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $group, $match)) {
2320d1286247SGreg Roach			$group = $this->vars[$match[1]]['id'];
2321a6f13a4aSGreg Roach			$group = trim($group);
2322a6f13a4aSGreg Roach		}
2323a6f13a4aSGreg Roach
2324a6f13a4aSGreg Roach		$id = "";
2325a6f13a4aSGreg Roach		if (isset($attrs['id'])) {
2326a6f13a4aSGreg Roach			$id = $attrs['id'];
2327a6f13a4aSGreg Roach		}
2328a6f13a4aSGreg Roach		if (preg_match("/\\$(\w+)/", $id, $match)) {
2329d1286247SGreg Roach			$id = $this->vars[$match[1]]['id'];
2330a6f13a4aSGreg Roach			$id = trim($id);
2331a6f13a4aSGreg Roach		}
2332a6f13a4aSGreg Roach
2333a6f13a4aSGreg Roach		$this->list = array();
2334a6f13a4aSGreg Roach		$person     = Individual::getInstance($id, $WT_TREE);
2335a6f13a4aSGreg Roach		if (!empty($person)) {
2336a6f13a4aSGreg Roach			$this->list[$id] = $person;
2337a6f13a4aSGreg Roach			switch ($group) {
2338a6f13a4aSGreg Roach				case "child-family":
2339a6f13a4aSGreg Roach					foreach ($person->getChildFamilies() as $family) {
2340a6f13a4aSGreg Roach						$husband = $family->getHusband();
2341a6f13a4aSGreg Roach						$wife    = $family->getWife();
2342a6f13a4aSGreg Roach						if (!empty($husband)) {
2343a6f13a4aSGreg Roach							$this->list[$husband->getXref()] = $husband;
2344a6f13a4aSGreg Roach						}
2345a6f13a4aSGreg Roach						if (!empty($wife)) {
2346a6f13a4aSGreg Roach							$this->list[$wife->getXref()] = $wife;
2347a6f13a4aSGreg Roach						}
2348a6f13a4aSGreg Roach						$children = $family->getChildren();
2349a6f13a4aSGreg Roach						foreach ($children as $child) {
2350a6f13a4aSGreg Roach							if (!empty($child)) {
2351a6f13a4aSGreg Roach								$this->list[$child->getXref()] = $child;
2352a6f13a4aSGreg Roach							}
2353a6f13a4aSGreg Roach						}
2354a6f13a4aSGreg Roach					}
2355a6f13a4aSGreg Roach					break;
2356a6f13a4aSGreg Roach				case "spouse-family":
2357a6f13a4aSGreg Roach					foreach ($person->getSpouseFamilies() as $family) {
2358a6f13a4aSGreg Roach						$husband = $family->getHusband();
2359a6f13a4aSGreg Roach						$wife    = $family->getWife();
2360a6f13a4aSGreg Roach						if (!empty($husband)) {
2361a6f13a4aSGreg Roach							$this->list[$husband->getXref()] = $husband;
2362a6f13a4aSGreg Roach						}
2363a6f13a4aSGreg Roach						if (!empty($wife)) {
2364a6f13a4aSGreg Roach							$this->list[$wife->getXref()] = $wife;
2365a6f13a4aSGreg Roach						}
2366a6f13a4aSGreg Roach						$children = $family->getChildren();
2367a6f13a4aSGreg Roach						foreach ($children as $child) {
2368a6f13a4aSGreg Roach							if (!empty($child)) {
2369a6f13a4aSGreg Roach								$this->list[$child->getXref()] = $child;
2370a6f13a4aSGreg Roach							}
2371a6f13a4aSGreg Roach						}
2372a6f13a4aSGreg Roach					}
2373a6f13a4aSGreg Roach					break;
2374a6f13a4aSGreg Roach				case "direct-ancestors":
23753d7a8a4cSGreg Roach					$this->addAncestors($this->list, $id, false, $maxgen);
2376a6f13a4aSGreg Roach					break;
2377a6f13a4aSGreg Roach				case "ancestors":
23783d7a8a4cSGreg Roach					$this->addAncestors($this->list, $id, true, $maxgen);
2379a6f13a4aSGreg Roach					break;
2380a6f13a4aSGreg Roach				case "descendants":
2381a6f13a4aSGreg Roach					$this->list[$id]->generation = 1;
23823d7a8a4cSGreg Roach					$this->addDescendancy($this->list, $id, false, $maxgen);
2383a6f13a4aSGreg Roach					break;
2384a6f13a4aSGreg Roach				case "all":
23853d7a8a4cSGreg Roach					$this->addAncestors($this->list, $id, true, $maxgen);
23863d7a8a4cSGreg Roach					$this->addDescendancy($this->list, $id, true, $maxgen);
2387a6f13a4aSGreg Roach					break;
2388a6f13a4aSGreg Roach			}
2389a6f13a4aSGreg Roach		}
2390a6f13a4aSGreg Roach
2391a6f13a4aSGreg Roach		switch ($sortby) {
2392a6f13a4aSGreg Roach			case 'NAME':
2393a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2394a6f13a4aSGreg Roach				break;
2395a6f13a4aSGreg Roach			case 'BIRT:DATE':
2396a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2397a6f13a4aSGreg Roach				break;
2398a6f13a4aSGreg Roach			case 'DEAT:DATE':
2399a6f13a4aSGreg Roach				uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2400a6f13a4aSGreg Roach				break;
2401a6f13a4aSGreg Roach			case 'generation':
2402a6f13a4aSGreg Roach				$newarray = array();
2403a6f13a4aSGreg Roach				reset($this->list);
2404a6f13a4aSGreg Roach				$genCounter = 1;
2405a6f13a4aSGreg Roach				while (count($newarray) < count($this->list)) {
2406a6f13a4aSGreg Roach					foreach ($this->list as $key => $value) {
2407a6f13a4aSGreg Roach						$this->generation = $value->generation;
2408a6f13a4aSGreg Roach						if ($this->generation == $genCounter) {
2409a6f13a4aSGreg Roach							$newarray[$key]             = new \stdClass;
2410a6f13a4aSGreg Roach							$newarray[$key]->generation = $this->generation;
2411a6f13a4aSGreg Roach						}
2412a6f13a4aSGreg Roach					}
2413a6f13a4aSGreg Roach					$genCounter++;
2414a6f13a4aSGreg Roach				}
2415a6f13a4aSGreg Roach				$this->list = $newarray;
2416a6f13a4aSGreg Roach				break;
2417a6f13a4aSGreg Roach			default:
2418a6f13a4aSGreg Roach				// unsorted
2419a6f13a4aSGreg Roach				break;
2420a6f13a4aSGreg Roach		}
2421a6f13a4aSGreg Roach		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2422e8e7866bSGreg Roach		$this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2423a6f13a4aSGreg Roach	}
2424a6f13a4aSGreg Roach
2425a6f13a4aSGreg Roach	/**
242676692c8bSGreg Roach	 * XML </ Relatives>
2427a6f13a4aSGreg Roach	 */
24288edd1043SGreg Roach	private function relativesEndHandler() {
2429e8e7866bSGreg Roach		global $report, $WT_TREE;
2430a6f13a4aSGreg Roach
2431a6f13a4aSGreg Roach		$this->process_repeats--;
2432a6f13a4aSGreg Roach		if ($this->process_repeats > 0) {
2433a6f13a4aSGreg Roach			return;
2434a6f13a4aSGreg Roach		}
2435a6f13a4aSGreg Roach
2436a6f13a4aSGreg Roach		// Check if there is any relatives
2437a6f13a4aSGreg Roach		if (count($this->list) > 0) {
2438a6f13a4aSGreg Roach			$lineoffset = 0;
2439a6f13a4aSGreg Roach			foreach ($this->repeats_stack as $rep) {
2440a6f13a4aSGreg Roach				$lineoffset += $rep[1];
2441a6f13a4aSGreg Roach			}
2442a6f13a4aSGreg Roach			//-- read the xml from the file
2443a6f13a4aSGreg Roach			$lines = file($report);
2444a6f13a4aSGreg Roach			while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<Relatives") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2445a6f13a4aSGreg Roach				$lineoffset--;
2446a6f13a4aSGreg Roach			}
2447a6f13a4aSGreg Roach			$lineoffset++;
2448a6f13a4aSGreg Roach			$reportxml = "<tempdoc>\n";
2449a6f13a4aSGreg Roach			$line_nr   = $lineoffset + $this->repeat_bytes;
2450a6f13a4aSGreg Roach			// Relatives Level counter
2451a6f13a4aSGreg Roach			$count = 1;
2452a6f13a4aSGreg Roach			while (0 < $count) {
2453a6f13a4aSGreg Roach				if (strpos($lines[$line_nr], "<Relatives") !== false) {
2454a6f13a4aSGreg Roach					$count++;
2455a6f13a4aSGreg Roach				} elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
2456a6f13a4aSGreg Roach					$count--;
2457a6f13a4aSGreg Roach				}
2458a6f13a4aSGreg Roach				if (0 < $count) {
2459a6f13a4aSGreg Roach					$reportxml .= $lines[$line_nr];
2460a6f13a4aSGreg Roach				}
2461a6f13a4aSGreg Roach				$line_nr++;
2462a6f13a4aSGreg Roach			}
2463a6f13a4aSGreg Roach			// No need to drag this
2464a6f13a4aSGreg Roach			unset($lines);
2465a6f13a4aSGreg Roach			$reportxml .= "</tempdoc>\n";
2466a6f13a4aSGreg Roach			// Save original values
2467e8e7866bSGreg Roach			array_push($this->parser_stack, $this->parser);
2468a6f13a4aSGreg Roach			$oldgedrec = $this->gedrec;
2469a6f13a4aSGreg Roach
2470a6f13a4aSGreg Roach			$this->list_total   = count($this->list);
2471a6f13a4aSGreg Roach			$this->list_private = 0;
2472a6f13a4aSGreg Roach			foreach ($this->list as $key => $value) {
2473a6f13a4aSGreg Roach				if (isset($value->generation)) {
2474a6f13a4aSGreg Roach					$this->generation = $value->generation;
2475a6f13a4aSGreg Roach				}
2476a6f13a4aSGreg Roach				$tmp          = GedcomRecord::getInstance($key, $WT_TREE);
2477a6f13a4aSGreg Roach				$this->gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
2478a6f13a4aSGreg Roach
2479a6f13a4aSGreg Roach				$repeat_parser = xml_parser_create();
2480e8e7866bSGreg Roach				$this->parser  = $repeat_parser;
2481a6f13a4aSGreg Roach				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2482a6f13a4aSGreg Roach				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2483a6f13a4aSGreg Roach				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2484a6f13a4aSGreg Roach
2485a6f13a4aSGreg Roach				if (!xml_parse($repeat_parser, $reportxml, true)) {
2486a6f13a4aSGreg 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)));
2487a6f13a4aSGreg Roach				}
2488a6f13a4aSGreg Roach				xml_parser_free($repeat_parser);
2489a6f13a4aSGreg Roach			}
2490a6f13a4aSGreg Roach			// Clean up the list array
2491a6f13a4aSGreg Roach			$this->list   = array();
2492e8e7866bSGreg Roach			$this->parser = array_pop($this->parser_stack);
2493a6f13a4aSGreg Roach			$this->gedrec = $oldgedrec;
2494a6f13a4aSGreg Roach		}
2495a6f13a4aSGreg Roach		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2496a6f13a4aSGreg Roach	}
2497a6f13a4aSGreg Roach
2498a6f13a4aSGreg Roach	/**
2499a6f13a4aSGreg Roach	 * XML <Generation /> element handler
2500a6f13a4aSGreg Roach	 *
2501a6f13a4aSGreg Roach	 * Prints the number of generations
2502a6f13a4aSGreg Roach	 */
25038edd1043SGreg Roach	private function generationStartHandler() {
2504a6f13a4aSGreg Roach		$this->current_element->addText($this->generation);
2505a6f13a4aSGreg Roach	}
2506a6f13a4aSGreg Roach
2507a6f13a4aSGreg Roach	/**
2508a6f13a4aSGreg Roach	 * XML <NewPage /> element handler
2509a6f13a4aSGreg Roach	 *
2510a6f13a4aSGreg Roach	 * Has to be placed in an element (header, pageheader, body or footer)
2511a6f13a4aSGreg Roach	 */
25128edd1043SGreg Roach	private function newPageStartHandler() {
2513a6f13a4aSGreg Roach		$temp = "addpage";
2514e8e7866bSGreg Roach		$this->wt_report->addElement($temp);
2515a6f13a4aSGreg Roach	}
2516a6f13a4aSGreg Roach
2517a6f13a4aSGreg Roach	/**
251876692c8bSGreg Roach	 * XML <html>
251976692c8bSGreg Roach	 *
2520a6f13a4aSGreg Roach	 * @param string  $tag   HTML tag name
252176692c8bSGreg Roach	 * @param array[] $attrs an array of key value pairs for the attributes
2522a6f13a4aSGreg Roach	 */
25238edd1043SGreg Roach	private function htmlStartHandler($tag, $attrs) {
2524a6f13a4aSGreg Roach		if ($tag === "tempdoc") {
2525a6f13a4aSGreg Roach			return;
2526a6f13a4aSGreg Roach		}
2527e8e7866bSGreg Roach		array_push($this->wt_report_stack, $this->wt_report);
2528e8e7866bSGreg Roach		$this->wt_report       = $this->report_root->createHTML($tag, $attrs);
2529e8e7866bSGreg Roach		$this->current_element = $this->wt_report;
2530a6f13a4aSGreg Roach
2531a6f13a4aSGreg Roach		array_push($this->print_data_stack, $this->print_data);
2532a6f13a4aSGreg Roach		$this->print_data = true;
2533a6f13a4aSGreg Roach	}
2534a6f13a4aSGreg Roach
2535a6f13a4aSGreg Roach	/**
253676692c8bSGreg Roach	 * XML </html>
253776692c8bSGreg Roach	 *
2538a6f13a4aSGreg Roach	 * @param string $tag
2539a6f13a4aSGreg Roach	 */
25408edd1043SGreg Roach	private function htmlEndHandler($tag) {
2541a6f13a4aSGreg Roach		if ($tag === "tempdoc") {
2542a6f13a4aSGreg Roach			return;
2543a6f13a4aSGreg Roach		}
2544a6f13a4aSGreg Roach
2545a6f13a4aSGreg Roach		$this->print_data      = array_pop($this->print_data_stack);
2546e8e7866bSGreg Roach		$this->current_element = $this->wt_report;
2547e8e7866bSGreg Roach		$this->wt_report       = array_pop($this->wt_report_stack);
2548e8e7866bSGreg Roach		if (!is_null($this->wt_report)) {
2549e8e7866bSGreg Roach			$this->wt_report->addElement($this->current_element);
2550a6f13a4aSGreg Roach		} else {
2551e8e7866bSGreg Roach			$this->wt_report = $this->current_element;
2552a6f13a4aSGreg Roach		}
2553a6f13a4aSGreg Roach	}
2554a6f13a4aSGreg Roach
2555a6f13a4aSGreg Roach	/**
2556a6f13a4aSGreg Roach	 * Handle <Input>
2557a6f13a4aSGreg Roach	 */
25588edd1043SGreg Roach	private function inputStartHandler() {
2559a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlStartHandler() being called
2560a6f13a4aSGreg Roach	}
2561a6f13a4aSGreg Roach
2562a6f13a4aSGreg Roach	/**
2563a6f13a4aSGreg Roach	 * Handle </Input>
2564a6f13a4aSGreg Roach	 */
25658edd1043SGreg Roach	private function inputEndHandler() {
2566a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlEndHandler() being called
2567a6f13a4aSGreg Roach	}
2568a6f13a4aSGreg Roach
2569a6f13a4aSGreg Roach	/**
2570a6f13a4aSGreg Roach	 * Handle <Report>
2571a6f13a4aSGreg Roach	 */
25728edd1043SGreg Roach	private function reportStartHandler() {
2573a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlStartHandler() being called
2574a6f13a4aSGreg Roach	}
2575a6f13a4aSGreg Roach
2576a6f13a4aSGreg Roach	/**
2577a6f13a4aSGreg Roach	 * Handle </Report>
2578a6f13a4aSGreg Roach	 */
25798edd1043SGreg Roach	private function reportEndHandler() {
2580a6f13a4aSGreg Roach		// Dummy function, to prevent the default HtmlEndHandler() being called
2581a6f13a4aSGreg Roach	}
2582a6f13a4aSGreg Roach
2583a6f13a4aSGreg Roach	/**
258476692c8bSGreg Roach	 * XML </titleEndHandler>
2585a6f13a4aSGreg Roach	 */
25868edd1043SGreg Roach	private function titleEndHandler() {
25872836aa05SGreg Roach		$this->report_root->addTitle($this->text);
2588a6f13a4aSGreg Roach	}
2589a6f13a4aSGreg Roach
2590a6f13a4aSGreg Roach	/**
259176692c8bSGreg Roach	 * XML </descriptionEndHandler>
2592a6f13a4aSGreg Roach	 */
25938edd1043SGreg Roach	private function descriptionEndHandler() {
25942836aa05SGreg Roach		$this->report_root->addDescription($this->text);
2595a6f13a4aSGreg Roach	}
2596729ce104SGreg Roach
2597729ce104SGreg Roach	/**
259876692c8bSGreg Roach	 * Create a list of all descendants.
259976692c8bSGreg Roach	 *
2600729ce104SGreg Roach	 * @param string[] $list
2601729ce104SGreg Roach	 * @param string   $pid
2602729ce104SGreg Roach	 * @param bool  $parents
2603729ce104SGreg Roach	 * @param int  $generations
2604729ce104SGreg Roach	 */
260582759250SGreg Roach	private function addDescendancy(&$list, $pid, $parents = false, $generations = -1) {
2606729ce104SGreg Roach		global $WT_TREE;
2607729ce104SGreg Roach
2608729ce104SGreg Roach		$person = Individual::getInstance($pid, $WT_TREE);
2609729ce104SGreg Roach		if ($person === null) {
2610729ce104SGreg Roach			return;
2611729ce104SGreg Roach		}
2612729ce104SGreg Roach		if (!isset($list[$pid])) {
2613729ce104SGreg Roach			$list[$pid] = $person;
2614729ce104SGreg Roach		}
2615729ce104SGreg Roach		if (!isset($list[$pid]->generation)) {
2616729ce104SGreg Roach			$list[$pid]->generation = 0;
2617729ce104SGreg Roach		}
2618729ce104SGreg Roach		foreach ($person->getSpouseFamilies() as $family) {
2619729ce104SGreg Roach			if ($parents) {
2620729ce104SGreg Roach				$husband = $family->getHusband();
2621729ce104SGreg Roach				$wife    = $family->getWife();
2622729ce104SGreg Roach				if ($husband) {
2623729ce104SGreg Roach					$list[$husband->getXref()] = $husband;
2624729ce104SGreg Roach					if (isset($list[$pid]->generation)) {
2625729ce104SGreg Roach						$list[$husband->getXref()]->generation = $list[$pid]->generation - 1;
2626729ce104SGreg Roach					} else {
2627729ce104SGreg Roach						$list[$husband->getXref()]->generation = 1;
2628729ce104SGreg Roach					}
2629729ce104SGreg Roach				}
2630729ce104SGreg Roach				if ($wife) {
2631729ce104SGreg Roach					$list[$wife->getXref()] = $wife;
2632729ce104SGreg Roach					if (isset($list[$pid]->generation)) {
2633729ce104SGreg Roach						$list[$wife->getXref()]->generation = $list[$pid]->generation - 1;
2634729ce104SGreg Roach					} else {
2635729ce104SGreg Roach						$list[$wife->getXref()]->generation = 1;
2636729ce104SGreg Roach					}
2637729ce104SGreg Roach				}
2638729ce104SGreg Roach			}
2639729ce104SGreg Roach			$children = $family->getChildren();
2640729ce104SGreg Roach			foreach ($children as $child) {
2641729ce104SGreg Roach				if ($child) {
2642729ce104SGreg Roach					$list[$child->getXref()] = $child;
2643729ce104SGreg Roach					if (isset($list[$pid]->generation)) {
2644729ce104SGreg Roach						$list[$child->getXref()]->generation = $list[$pid]->generation + 1;
2645729ce104SGreg Roach					} else {
2646729ce104SGreg Roach						$list[$child->getXref()]->generation = 2;
2647729ce104SGreg Roach					}
2648729ce104SGreg Roach				}
2649729ce104SGreg Roach			}
2650729ce104SGreg Roach			if ($generations == -1 || $list[$pid]->generation + 1 < $generations) {
2651729ce104SGreg Roach				foreach ($children as $child) {
26523d7a8a4cSGreg Roach					$this->addDescendancy($list, $child->getXref(), $parents, $generations); // recurse on the childs family
2653729ce104SGreg Roach				}
2654729ce104SGreg Roach			}
2655729ce104SGreg Roach		}
2656729ce104SGreg Roach	}
2657729ce104SGreg Roach
2658729ce104SGreg Roach	/**
265976692c8bSGreg Roach	 * Create a list of all ancestors.
266076692c8bSGreg Roach	 *
2661729ce104SGreg Roach	 * @param string[] $list
2662729ce104SGreg Roach	 * @param string   $pid
2663729ce104SGreg Roach	 * @param bool  $children
2664729ce104SGreg Roach	 * @param int  $generations
2665729ce104SGreg Roach	 */
266682759250SGreg Roach	private function addAncestors(&$list, $pid, $children = false, $generations = -1) {
2667729ce104SGreg Roach		global $WT_TREE;
2668729ce104SGreg Roach
2669729ce104SGreg Roach		$genlist                = array($pid);
2670729ce104SGreg Roach		$list[$pid]->generation = 1;
2671729ce104SGreg Roach		while (count($genlist) > 0) {
2672729ce104SGreg Roach			$id = array_shift($genlist);
2673729ce104SGreg Roach			if (strpos($id, 'empty') === 0) {
2674729ce104SGreg Roach				continue; // id can be something like “empty7”
2675729ce104SGreg Roach			}
2676729ce104SGreg Roach			$person = Individual::getInstance($id, $WT_TREE);
2677729ce104SGreg Roach			foreach ($person->getChildFamilies() as $family) {
2678729ce104SGreg Roach				$husband = $family->getHusband();
2679729ce104SGreg Roach				$wife    = $family->getWife();
2680729ce104SGreg Roach				if ($husband) {
2681729ce104SGreg Roach					$list[$husband->getXref()]             = $husband;
2682729ce104SGreg Roach					$list[$husband->getXref()]->generation = $list[$id]->generation + 1;
2683729ce104SGreg Roach				}
2684729ce104SGreg Roach				if ($wife) {
2685729ce104SGreg Roach					$list[$wife->getXref()]             = $wife;
2686729ce104SGreg Roach					$list[$wife->getXref()]->generation = $list[$id]->generation + 1;
2687729ce104SGreg Roach				}
2688729ce104SGreg Roach				if ($generations == -1 || $list[$id]->generation + 1 < $generations) {
2689729ce104SGreg Roach					if ($husband) {
2690729ce104SGreg Roach						array_push($genlist, $husband->getXref());
2691729ce104SGreg Roach					}
2692729ce104SGreg Roach					if ($wife) {
2693729ce104SGreg Roach						array_push($genlist, $wife->getXref());
2694729ce104SGreg Roach					}
2695729ce104SGreg Roach				}
2696729ce104SGreg Roach				if ($children) {
2697729ce104SGreg Roach					foreach ($family->getChildren() as $child) {
2698729ce104SGreg Roach						$list[$child->getXref()] = $child;
2699729ce104SGreg Roach						if (isset($list[$id]->generation)) {
2700729ce104SGreg Roach							$list[$child->getXref()]->generation = $list[$id]->generation;
2701729ce104SGreg Roach						} else {
2702729ce104SGreg Roach							$list[$child->getXref()]->generation = 1;
2703729ce104SGreg Roach						}
2704729ce104SGreg Roach					}
2705729ce104SGreg Roach				}
2706729ce104SGreg Roach			}
2707729ce104SGreg Roach		}
2708729ce104SGreg Roach	}
2709729ce104SGreg Roach
2710729ce104SGreg Roach	/**
2711729ce104SGreg Roach	 * get gedcom tag value
2712729ce104SGreg Roach	 *
2713729ce104SGreg Roach	 * @param string  $tag    The tag to find, use : to delineate subtags
2714729ce104SGreg Roach	 * @param int $level  The gedcom line level of the first tag to find, setting level to 0 will cause it to use 1+ the level of the incoming record
2715729ce104SGreg Roach	 * @param string  $gedrec The gedcom record to get the value from
2716729ce104SGreg Roach	 *
2717729ce104SGreg Roach	 * @return string the value of a gedcom tag from the given gedcom record
2718729ce104SGreg Roach	 */
271982759250SGreg Roach	private function getGedcomValue($tag, $level, $gedrec) {
2720729ce104SGreg Roach		global $WT_TREE;
2721729ce104SGreg Roach
2722729ce104SGreg Roach		if (empty($gedrec)) {
2723729ce104SGreg Roach			return '';
2724729ce104SGreg Roach		}
2725729ce104SGreg Roach		$tags      = explode(':', $tag);
2726729ce104SGreg Roach		$origlevel = $level;
2727729ce104SGreg Roach		if ($level == 0) {
2728729ce104SGreg Roach			$level = $gedrec{0} + 1;
2729729ce104SGreg Roach		}
2730729ce104SGreg Roach
2731729ce104SGreg Roach		$subrec = $gedrec;
2732729ce104SGreg Roach		foreach ($tags as $t) {
2733729ce104SGreg Roach			$lastsubrec = $subrec;
27343d7a8a4cSGreg Roach			$subrec     = Functions::getSubRecord($level, "$level $t", $subrec);
2735729ce104SGreg Roach			if (empty($subrec) && $origlevel == 0) {
2736729ce104SGreg Roach				$level--;
27373d7a8a4cSGreg Roach				$subrec = Functions::getSubRecord($level, "$level $t", $lastsubrec);
2738729ce104SGreg Roach			}
2739729ce104SGreg Roach			if (empty($subrec)) {
2740729ce104SGreg Roach				if ($t == "TITL") {
27413d7a8a4cSGreg Roach					$subrec = Functions::getSubRecord($level, "$level ABBR", $lastsubrec);
2742729ce104SGreg Roach					if (!empty($subrec)) {
2743729ce104SGreg Roach						$t = "ABBR";
2744729ce104SGreg Roach					}
2745729ce104SGreg Roach				}
2746729ce104SGreg Roach				if (empty($subrec)) {
2747729ce104SGreg Roach					if ($level > 0) {
2748729ce104SGreg Roach						$level--;
2749729ce104SGreg Roach					}
27503d7a8a4cSGreg Roach					$subrec = Functions::getSubRecord($level, "@ $t", $gedrec);
2751729ce104SGreg Roach					if (empty($subrec)) {
2752729ce104SGreg Roach						return '';
2753729ce104SGreg Roach					}
2754729ce104SGreg Roach				}
2755729ce104SGreg Roach			}
2756729ce104SGreg Roach			$level++;
2757729ce104SGreg Roach		}
2758729ce104SGreg Roach		$level--;
2759729ce104SGreg Roach		$ct = preg_match("/$level $t(.*)/", $subrec, $match);
2760729ce104SGreg Roach		if ($ct == 0) {
2761729ce104SGreg Roach			$ct = preg_match("/$level @.+@ (.+)/", $subrec, $match);
2762729ce104SGreg Roach		}
2763729ce104SGreg Roach		if ($ct == 0) {
2764729ce104SGreg Roach			$ct = preg_match("/@ $t (.+)/", $subrec, $match);
2765729ce104SGreg Roach		}
2766729ce104SGreg Roach		if ($ct > 0) {
2767729ce104SGreg Roach			$value = trim($match[1]);
2768729ce104SGreg Roach			if ($t == 'NOTE' && preg_match('/^@(.+)@$/', $value, $match)) {
2769729ce104SGreg Roach				$note = Note::getInstance($match[1], $WT_TREE);
2770729ce104SGreg Roach				if ($note) {
2771729ce104SGreg Roach					$value = $note->getNote();
2772729ce104SGreg Roach				} else {
2773729ce104SGreg Roach					//-- set the value to the id without the @
2774729ce104SGreg Roach					$value = $match[1];
2775729ce104SGreg Roach				}
2776729ce104SGreg Roach			}
2777729ce104SGreg Roach			if ($level != 0 || $t != "NOTE") {
27783d7a8a4cSGreg Roach				$value .= Functions::getCont($level + 1, $subrec);
2779729ce104SGreg Roach			}
2780729ce104SGreg Roach
2781729ce104SGreg Roach			return $value;
2782729ce104SGreg Roach		}
2783729ce104SGreg Roach
2784729ce104SGreg Roach		return "";
2785729ce104SGreg Roach	}
2786d1286247SGreg Roach
2787d1286247SGreg Roach	/**
2788d1286247SGreg Roach	 * Replace variable identifiers with their values.
2789d1286247SGreg Roach	 *
2790d1286247SGreg Roach	 * @param string $expression An expression such as "$foo == 123"
279182759250SGreg Roach	 * @param bool   $quote      Whether to add quotation marks
2792d1286247SGreg Roach	 *
2793d1286247SGreg Roach	 * @return string
2794d1286247SGreg Roach	 */
279582759250SGreg Roach	private function substituteVars($expression, $quote) {
27965217901bSGreg Roach		$that = $this; // PHP5.3 cannot access $this inside a closure
2797d1286247SGreg Roach		return preg_replace_callback(
2798d1286247SGreg Roach			'/\$(\w+)/',
27995217901bSGreg Roach			function ($matches) use ($that, $quote) {
28005217901bSGreg Roach				if (isset($that->vars[$matches[1]]['id'])) {
280182759250SGreg Roach					if ($quote) {
28025217901bSGreg Roach						return "'" . addcslashes($that->vars[$matches[1]]['id'], "'") . "'";
280382759250SGreg Roach					} else {
28045217901bSGreg Roach						return $that->vars[$matches[1]]['id'];
280582759250SGreg Roach					}
2806d1286247SGreg Roach				} else {
2807d1286247SGreg Roach					Log::addErrorLog(sprintf('Undefined variable $%s in report', $matches[1]));
28083d7a8a4cSGreg Roach
2809d1286247SGreg Roach					return '$' . $matches[1];
2810d1286247SGreg Roach				}
2811d1286247SGreg Roach			},
2812d1286247SGreg Roach			$expression
2813d1286247SGreg Roach		);
2814d1286247SGreg Roach	}
2815a6f13a4aSGreg Roach}
2816