<?php

namespace PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;

// vim: set expandtab tabstop=4 shiftwidth=4:
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license,      |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Author: Xavier Noguer <xnoguer@php.net>                              |
// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Shared\OLE;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;

/**
 * Class for creating Root PPS's for OLE containers.
 *
 * @author   Xavier Noguer <xnoguer@php.net>
 *
 * @category PhpSpreadsheet
 */
class Root extends PPS
{
    /**
     * Directory for temporary files.
     *
     * @var string
     */
    protected $tempDirectory;

    /**
     * @var resource
     */
    private $fileHandle;

    /**
     * @var string
     */
    private $tempFilename;

    /**
     * @var int
     */
    private $smallBlockSize;

    /**
     * @var int
     */
    private $bigBlockSize;

    /**
     * @param int $time_1st A timestamp
     * @param int $time_2nd A timestamp
     * @param File[] $raChild
     */
    public function __construct($time_1st, $time_2nd, $raChild)
    {
        $this->tempDirectory = \PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir();

        parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
    }

    /**
     * Method for saving the whole OLE container (including files).
     * In fact, if called with an empty argument (or '-'), it saves to a
     * temporary file and then outputs it's contents to stdout.
     * If a resource pointer to a stream created by fopen() is passed
     * it will be used, but you have to close such stream by yourself.
     *
     * @param resource|string $filename the name of the file or stream where to save the OLE container
     *
     * @throws WriterException
     *
     * @return bool true on success
     */
    public function save($filename)
    {
        // Initial Setting for saving
        $this->bigBlockSize = pow(
            2,
            (isset($this->bigBlockSize)) ? self::adjust2($this->bigBlockSize) : 9
        );
        $this->smallBlockSize = pow(
            2,
            (isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6
        );

        if (is_resource($filename)) {
            $this->fileHandle = $filename;
        } elseif ($filename == '-' || $filename == '') {
            if ($this->tempDirectory === null) {
                $this->tempDirectory = \PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir();
            }
            $this->tempFilename = tempnam($this->tempDirectory, 'OLE_PPS_Root');
            $this->fileHandle = fopen($this->tempFilename, 'w+b');
            if ($this->fileHandle == false) {
                throw new WriterException("Can't create temporary file.");
            }
        } else {
            $this->fileHandle = fopen($filename, 'wb');
        }
        if ($this->fileHandle == false) {
            throw new WriterException("Can't open $filename. It may be in use or protected.");
        }
        // Make an array of PPS's (for Save)
        $aList = [];
        PPS::_savePpsSetPnt($aList, [$this]);
        // calculate values for header
        list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo);
        // Save Header
        $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);

        // Make Small Data string (write SBD)
        $this->_data = $this->_makeSmallData($aList);

        // Write BB
        $this->_saveBigData($iSBDcnt, $aList);
        // Write PPS
        $this->_savePps($aList);
        // Write Big Block Depot and BDList and Adding Header informations
        $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);

        if (!is_resource($filename)) {
            fclose($this->fileHandle);
        }

        return true;
    }

    /**
     * Calculate some numbers.
     *
     * @param array $raList Reference to an array of PPS's
     *
     * @return float[] The array of numbers
     */
    public function _calcSize(&$raList)
    {
        // Calculate Basic Setting
        list($iSBDcnt, $iBBcnt, $iPPScnt) = [0, 0, 0];
        $iSmallLen = 0;
        $iSBcnt = 0;
        $iCount = count($raList);
        for ($i = 0; $i < $iCount; ++$i) {
            if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
                $raList[$i]->Size = $raList[$i]->getDataLen();
                if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
                    $iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize)
                                  + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
                } else {
                    $iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize) +
                        (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
                }
            }
        }
        $iSmallLen = $iSBcnt * $this->smallBlockSize;
        $iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
        $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0);
        $iBBcnt += (floor($iSmallLen / $this->bigBlockSize) +
                      (($iSmallLen % $this->bigBlockSize) ? 1 : 0));
        $iCnt = count($raList);
        $iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
        $iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));

        return [$iSBDcnt, $iBBcnt, $iPPScnt];
    }

    /**
     * Helper function for caculating a magic value for block sizes.
     *
     * @param int $i2 The argument
     *
     * @see save()
     *
     * @return float
     */
    private static function adjust2($i2)
    {
        $iWk = log($i2) / log(2);

        return ($iWk > floor($iWk)) ? floor($iWk) + 1 : $iWk;
    }

    /**
     * Save OLE header.
     *
     * @param int $iSBDcnt
     * @param int $iBBcnt
     * @param int $iPPScnt
     */
    public function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt)
    {
        $FILE = $this->fileHandle;

        // Calculate Basic Setting
        $iBlCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
        $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;

        $iBdExL = 0;
        $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
        $iAllW = $iAll;
        $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
        $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);

        // Calculate BD count
        if ($iBdCnt > $i1stBdL) {
            while (1) {
                ++$iBdExL;
                ++$iAllW;
                $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
                $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
                if ($iBdCnt <= ($iBdExL * $iBlCnt + $i1stBdL)) {
                    break;
                }
            }
        }

        // Save Header
        fwrite(
            $FILE,
            "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
            . "\x00\x00\x00\x00"
            . "\x00\x00\x00\x00"
            . "\x00\x00\x00\x00"
            . "\x00\x00\x00\x00"
            . pack('v', 0x3b)
            . pack('v', 0x03)
            . pack('v', -2)
            . pack('v', 9)
            . pack('v', 6)
            . pack('v', 0)
            . "\x00\x00\x00\x00"
            . "\x00\x00\x00\x00"
            . pack('V', $iBdCnt)
            . pack('V', $iBBcnt + $iSBDcnt) //ROOT START
            . pack('V', 0)
            . pack('V', 0x1000)
            . pack('V', $iSBDcnt ? 0 : -2) //Small Block Depot
            . pack('V', $iSBDcnt)
        );
        // Extra BDList Start, Count
        if ($iBdCnt < $i1stBdL) {
            fwrite(
                $FILE,
                pack('V', -2) // Extra BDList Start
                . pack('V', 0)// Extra BDList Count
            );
        } else {
            fwrite($FILE, pack('V', $iAll + $iBdCnt) . pack('V', $iBdExL));
        }

        // BDList
        for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
            fwrite($FILE, pack('V', $iAll + $i));
        }
        if ($i < $i1stBdL) {
            $jB = $i1stBdL - $i;
            for ($j = 0; $j < $jB; ++$j) {
                fwrite($FILE, (pack('V', -1)));
            }
        }
    }

    /**
     * Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
     *
     * @param int $iStBlk
     * @param array &$raList Reference to array of PPS's
     */
    public function _saveBigData($iStBlk, &$raList)
    {
        $FILE = $this->fileHandle;

        // cycle through PPS's
        $iCount = count($raList);
        for ($i = 0; $i < $iCount; ++$i) {
            if ($raList[$i]->Type != OLE::OLE_PPS_TYPE_DIR) {
                $raList[$i]->Size = $raList[$i]->getDataLen();
                if (($raList[$i]->Size >= OLE::OLE_DATA_SIZE_SMALL) || (($raList[$i]->Type == OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) {
                    fwrite($FILE, $raList[$i]->_data);

                    if ($raList[$i]->Size % $this->bigBlockSize) {
                        fwrite($FILE, str_repeat("\x00", $this->bigBlockSize - ($raList[$i]->Size % $this->bigBlockSize)));
                    }
                    // Set For PPS
                    $raList[$i]->startBlock = $iStBlk;
                    $iStBlk +=
                            (floor($raList[$i]->Size / $this->bigBlockSize) +
                                (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
                }
            }
        }
    }

    /**
     * get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
     *
     * @param array &$raList Reference to array of PPS's
     *
     * @return string
     */
    public function _makeSmallData(&$raList)
    {
        $sRes = '';
        $FILE = $this->fileHandle;
        $iSmBlk = 0;

        $iCount = count($raList);
        for ($i = 0; $i < $iCount; ++$i) {
            // Make SBD, small data string
            if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
                if ($raList[$i]->Size <= 0) {
                    continue;
                }
                if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
                    $iSmbCnt = floor($raList[$i]->Size / $this->smallBlockSize)
                                  + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
                    // Add to SBD
                    $jB = $iSmbCnt - 1;
                    for ($j = 0; $j < $jB; ++$j) {
                        fwrite($FILE, pack('V', $j + $iSmBlk + 1));
                    }
                    fwrite($FILE, pack('V', -2));

                    // Add to Data String(this will be written for RootEntry)
                    $sRes .= $raList[$i]->_data;
                    if ($raList[$i]->Size % $this->smallBlockSize) {
                        $sRes .= str_repeat("\x00", $this->smallBlockSize - ($raList[$i]->Size % $this->smallBlockSize));
                    }
                    // Set for PPS
                    $raList[$i]->startBlock = $iSmBlk;
                    $iSmBlk += $iSmbCnt;
                }
            }
        }
        $iSbCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
        if ($iSmBlk % $iSbCnt) {
            $iB = $iSbCnt - ($iSmBlk % $iSbCnt);
            for ($i = 0; $i < $iB; ++$i) {
                fwrite($FILE, pack('V', -1));
            }
        }

        return $sRes;
    }

    /**
     * Saves all the PPS's WKs.
     *
     * @param array $raList Reference to an array with all PPS's
     */
    public function _savePps(&$raList)
    {
        // Save each PPS WK
        $iC = count($raList);
        for ($i = 0; $i < $iC; ++$i) {
            fwrite($this->fileHandle, $raList[$i]->_getPpsWk());
        }
        // Adjust for Block
        $iCnt = count($raList);
        $iBCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
        if ($iCnt % $iBCnt) {
            fwrite($this->fileHandle, str_repeat("\x00", ($iBCnt - ($iCnt % $iBCnt)) * OLE::OLE_PPS_SIZE));
        }
    }

    /**
     * Saving Big Block Depot.
     *
     * @param int $iSbdSize
     * @param int $iBsize
     * @param int $iPpsCnt
     */
    public function _saveBbd($iSbdSize, $iBsize, $iPpsCnt)
    {
        $FILE = $this->fileHandle;
        // Calculate Basic Setting
        $iBbCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
        $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;

        $iBdExL = 0;
        $iAll = $iBsize + $iPpsCnt + $iSbdSize;
        $iAllW = $iAll;
        $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
        $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
        // Calculate BD count
        if ($iBdCnt > $i1stBdL) {
            while (1) {
                ++$iBdExL;
                ++$iAllW;
                $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
                $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
                if ($iBdCnt <= ($iBdExL * $iBbCnt + $i1stBdL)) {
                    break;
                }
            }
        }

        // Making BD
        // Set for SBD
        if ($iSbdSize > 0) {
            for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
                fwrite($FILE, pack('V', $i + 1));
            }
            fwrite($FILE, pack('V', -2));
        }
        // Set for B
        for ($i = 0; $i < ($iBsize - 1); ++$i) {
            fwrite($FILE, pack('V', $i + $iSbdSize + 1));
        }
        fwrite($FILE, pack('V', -2));

        // Set for PPS
        for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
            fwrite($FILE, pack('V', $i + $iSbdSize + $iBsize + 1));
        }
        fwrite($FILE, pack('V', -2));
        // Set for BBD itself ( 0xFFFFFFFD : BBD)
        for ($i = 0; $i < $iBdCnt; ++$i) {
            fwrite($FILE, pack('V', 0xFFFFFFFD));
        }
        // Set for ExtraBDList
        for ($i = 0; $i < $iBdExL; ++$i) {
            fwrite($FILE, pack('V', 0xFFFFFFFC));
        }
        // Adjust for Block
        if (($iAllW + $iBdCnt) % $iBbCnt) {
            $iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt));
            for ($i = 0; $i < $iBlock; ++$i) {
                fwrite($FILE, pack('V', -1));
            }
        }
        // Extra BDList
        if ($iBdCnt > $i1stBdL) {
            $iN = 0;
            $iNb = 0;
            for ($i = $i1stBdL; $i < $iBdCnt; $i++, ++$iN) {
                if ($iN >= ($iBbCnt - 1)) {
                    $iN = 0;
                    ++$iNb;
                    fwrite($FILE, pack('V', $iAll + $iBdCnt + $iNb));
                }
                fwrite($FILE, pack('V', $iBsize + $iSbdSize + $iPpsCnt + $i));
            }
            if (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)) {
                $iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1));
                for ($i = 0; $i < $iB; ++$i) {
                    fwrite($FILE, pack('V', -1));
                }
            }
            fwrite($FILE, pack('V', -2));
        }
    }
}


<?php
/**
 * Service Bag
 */
namespace Omniship\Common;

use Omniship\Helper\Collection;
use Omniship\Interfaces\ServiceInterface;

/**
 * Shipping Service Bag
 *
 * This class defines a bag (multi element set or array) of single shipping items
 * in the Omniship system.
 *
 * @see Item
 */
class ServiceBag extends Collection
{

    /**
     * Create a new collection.
     *
     * @param  mixed  $items
     */
    public function __construct($items = [])
    {
        $items = array_map(function($item) {
            return !($item instanceof ServiceInterface) ? new Service($item) : $item;
        }, $items);
        parent::__construct($items);
    }

    /**
     * Set the item at a given offset.
     *
     * @param  mixed  $key
     * @param  mixed  $value
     * @return void
     */
    public function offsetSet($key, $value)
    {
        if(!($value instanceof ServiceInterface)) {
            $value = new Service($value);
        }
        parent::offsetSet($key, $value);
    }

    /**
     * Get all of the items in the collection.
     *
     * @return Service[]
     */
    public function all()
    {
        return parent::all();
    }
}


<?php
/**
 * Custom Images Grifus · Extensions For Grifus
 *
 * @author    Josantonius <hello@josantonius.com>
 * @package   eliasis-framework/custom-images-grifus
 * @copyright 2017 - 2018 (c) Josantonius - Custom Images Grifus
 * @license   GPL-2.0+
 * @link      https://github.com/eliasis-framework/custom-images-grifus.git
 * @since     1.0.0
 */

namespace EFG\Modules\CustomImagesGrifus\Model\Admin;

use Eliasis\Framework\Model;
use Josantonius\WP_Image\WP_Image;

/**
 * Image controller
 */
class Image extends Model {

	/**
	 * Find IMDB images, save them and replace them in posts.
	 *
	 * @param int $post_id → post id.
	 *
	 * @return string → posts edited number
	 */
	public function set_images( $post_id ) {

		if ( ! $post_id || is_null( $post_id ) ) {
			return 0; }

		update_post_meta( $post_id, 'custom_images_grifus', 'true' );
		$count  = 0;
		$tmdb   = 'image.tmdb.org';
		$poster = get_post_meta( $post_id, 'poster_url', true );

		if ( filter_var( $poster, FILTER_VALIDATE_URL ) && strpos( $poster, $tmdb ) ) {
			$count++;
			$poster = WP_Image::save( $poster, $post_id, true );
			update_post_meta( $post_id, 'poster_url', $poster );
		}

		$main = get_post_meta( $post_id, 'fondo_player', true );

		if ( filter_var( $main, FILTER_VALIDATE_URL ) && strpos( $main, $tmdb ) ) {
			$count++;
			$main = WP_Image::save( $main, $post_id );
			update_post_meta( $post_id, 'fondo_player', $main );
		}

		$images       = get_post_meta( $post_id, 'imagenes', true );
		$images_array = explode( "\n", $images );
		$new_images   = '';

		foreach ( $images_array as $image ) {
			$image = trim( $image );
			if ( filter_var( $image, FILTER_VALIDATE_URL ) && strpos( $image, $tmdb ) ) {
				$count++;
				$url = WP_Image::save( $image, $post_id );

				$new_images .= $url . "\n";
			}
		}

		if ( ! empty( $new_images ) ) {
			update_post_meta( $post_id, 'imagenes', $new_images );
		}

		return $count;
	}

	/**
	 * Search post not edited previously and replace images.
	 *
	 * @return array
	 */
	public function replace_old_images() {

		global $wpdb;

		$response = [
			'post_id'      => 0,
			'post_title'   => '',
			'images_added' => 0,
		];

		$post_table = $wpdb->prefix . 'posts';

		$postmeta_table = $wpdb->prefix . 'postmeta';

		$query = "
            SELECT     ID, post_title
            FROM       $post_table
            INNER JOIN $postmeta_table
            ON         $post_table.ID = $postmeta_table.post_id
            WHERE      $post_table.post_type = 'post'
            AND        $post_table.post_status = 'publish'
            AND        $postmeta_table.meta_key = 'custom_images_grifus'
            AND        $postmeta_table.meta_value = 'false'
            ORDER BY   $post_table.ID ASC
        ";

		$post = $wpdb->get_row( $query );

		if ( ! is_null( $post ) && isset( $post->ID ) && isset( $post->post_title ) ) {
			$post_id = $post->ID;

			$images_added = $this->set_images( $post->ID );

			$response = [
				'post_id'      => $post->ID,
				'post_title'   => $post->post_title,
				'images_added' => $images_added,
			];
		}

		return $response;
	}

	/**
	 * Set posts to review.
	 */
	public function set_posts_to_review() {

		$total_posts = wp_count_posts();
		$total_posts = isset( $total_posts->publish ) ? $total_posts->publish : 0;

		$posts = get_posts(
			[
				'post_type'   => 'post',
				'numberposts' => $total_posts,
				'post_status' => 'publish',
			]
		);

		foreach ( $posts as $post ) {
			if ( isset( $post->ID ) ) {
				add_post_meta(
					$post->ID,
					'custom_images_grifus',
					'false',
					true
				);
			}
		}
	}

	/**
	 * Set state for replace images when added a movie.
	 *
	 * @since 1.0.1
	 *
	 * @param string  $slug  → module slug.
	 * @param boolean $state → restart when added a movie.
	 */
	public function set_replace_when_add( $slug, $state ) {

		update_option( $slug . '-replace-when-add', $state );
	}

	/**
	 * Delete attached images.
	 *
	 * @since 1.0.1
	 *
	 * @param int $post_id → post ID.
	 *
	 * @return int → attachments deleted
	 */
	public function delete_attached_images( $post_id ) {

		return WP_Image::delete_attached_images( $post_id );
	}
}


<?php
/*   _            _          _ ____   ___  _____
 *  | |          | |        | |___ \ / _ \| ____|
 *  | |      __ _| |__   ___| | __) | | | | |__
 *  | |     / _` | '_ \ / _ \ ||__ <|  -  |___ \
 *  | |____| (_| | |_) |  __/ |___) |     |___) |
 *  |______|\__,_|_.__/ \___|_|____/ \___/|____/
 *
 *  Copyright Label305 B.V. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Label305\AujaLaravel\Factory;

use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\URL;
use Label305\Auja\Menu\LinkMenuItem;
use Label305\Auja\Menu\Resource;
use Label305\AujaLaravel\Config\AujaConfigurator;
use Label305\AujaLaravel\Config\ModelConfig;
use Label305\AujaLaravel\Config\Relation;
use Label305\AujaLaravel\Routing\AujaRouter;

class ResourceItemsFactory {

    /**
     * @var AujaConfigurator
     */
    private $aujaConfigurator;

    /**
     * @var AujaRouter
     */
    private $aujaRouter;

    public function __construct(AujaConfigurator $aujaConfigurator, AujaRouter $aujaRouter) {
        $this->aujaConfigurator = $aujaConfigurator;
        $this->aujaRouter = $aujaRouter;
    }

    /**
     * Builds a Resource instance for given items.
     * This is typically used when a ResourceMenuItem triggers a call for items.
     *
     * This method also supports pagination, either manually or automatically.
     * To automatically use pagination, simply provide a Paginator as items.
     *
     * @param String          $modelName   the name of the model the items represent.
     * @param array|Paginator $items       an array of instances of the model to be shown, or a Paginator containing the instances.
     * @param String          $targetUrl   (optional) The target url for the items. Must contain '%d' in the place of the item id.
     * @param String          $nextPageUrl (optional) The url to the next page, if any.
     * @param int             $offset      (optional) The offset to start the order from.
     * @param ModelConfig     $config      (optional) The `ModelConfig` to use.
     *
     * @return Resource The built LinkMenuItems.
     */
    public function create($modelName, $items, $targetUrl = null, $nextPageUrl = null, $offset = -1, ModelConfig $config = null) { // TODO: create separate methods for pagination and no pagination?
        /* Extract items from Paginator if necessary */
        $paginator = null;
        if ($items instanceof Paginator) {
            $paginator = $items;
            $items = $paginator->getCollection();

            if ($offset == -1) {
                $offset = ($paginator->getCurrentPage() - 1) * $paginator->getPerPage();
            }
        }

        /* If the offset is not set, use no offset */
        if ($offset == -1) {
            $offset = 0;
        }

        /* No items. */
        if (count($items) == 0) {
            return new Resource();
        }

        /* If the items are not iterable */
        if (!($items instanceof \IteratorAggregate)) {
            $items = new Collection([$items]);
        }

        $model = $this->aujaConfigurator->getModel($modelName);

        /* Find relations for this model, so we can know the target */
        $relations = $this->aujaConfigurator->getRelationsForModel($model);
        $associationRelations = array();
        foreach ($relations as $relation) {
            if ($relation->getType() == Relation::HAS_MANY || $relation->getType() == Relation::HAS_AND_BELONGS_TO) {
                $associationRelations[] = $relation;
            }
        }

        /* Build the actual items to return */
        $resourceItems = new Resource();
        $displayField = $this->aujaConfigurator->getDisplayField($model, $config);
        $icon = $this->aujaConfigurator->getIcon($model, $config);
        for ($i = 0; $i < count($items); $i++) {
            if ($targetUrl != null) {
                $target = sprintf($targetUrl, $items[$i]->id);
            } else if (count($associationRelations) == 0) {
                $target = URL::route($this->aujaRouter->getEditName($modelName), $items[$i]->id);
            } else {
                $target = URL::route($this->aujaRouter->getShowMenuName($modelName), $items[$i]->id);
            }

            $menuItem = new LinkMenuItem();
            $menuItem->setText($items[$i]->$displayField);
            $menuItem->setTarget($target);
            $menuItem->setOrder($offset + $i);
            $menuItem->setIcon($icon);
            $resourceItems->addItem($menuItem);
        }

        /* Add pagination if necessary */
        if ($nextPageUrl != null) {
            $resourceItems->setNextPageUrl($nextPageUrl);
        } else if ($paginator != null && $paginator->getCurrentPage() != $paginator->getLastPage()) {
            $target = route($this->aujaRouter->getIndexName($modelName), ['page' => ($paginator->getCurrentPage() + 1)]);
            $resourceItems->setNextPageUrl($target);
        }

        return $resourceItems;
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Shared;

use InvalidArgumentException;
use ZipArchive;

class File
{
    /**
     * Use Temp or File Upload Temp for temporary files.
     *
     * @var bool
     */
    protected static $useUploadTempDirectory = false;

    /**
     * Set the flag indicating whether the File Upload Temp directory should be used for temporary files.
     *
     * @param bool $useUploadTempDir Use File Upload Temporary directory (true or false)
     */
    public static function setUseUploadTempDirectory($useUploadTempDir)
    {
        self::$useUploadTempDirectory = (bool) $useUploadTempDir;
    }

    /**
     * Get the flag indicating whether the File Upload Temp directory should be used for temporary files.
     *
     * @return bool Use File Upload Temporary directory (true or false)
     */
    public static function getUseUploadTempDirectory()
    {
        return self::$useUploadTempDirectory;
    }

    /**
     * Verify if a file exists.
     *
     * @param string $pFilename Filename
     *
     * @return bool
     */
    public static function fileExists($pFilename)
    {
        // Sick construction, but it seems that
        // file_exists returns strange values when
        // doing the original file_exists on ZIP archives...
        if (strtolower(substr($pFilename, 0, 3)) == 'zip') {
            // Open ZIP file and verify if the file exists
            $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
            $archiveFile = substr($pFilename, strpos($pFilename, '#') + 1);

            $zip = new ZipArchive();
            if ($zip->open($zipFile) === true) {
                $returnValue = ($zip->getFromName($archiveFile) !== false);
                $zip->close();

                return $returnValue;
            }

            return false;
        }

        return file_exists($pFilename);
    }

    /**
     * Returns canonicalized absolute pathname, also for ZIP archives.
     *
     * @param string $pFilename
     *
     * @return string
     */
    public static function realpath($pFilename)
    {
        // Returnvalue
        $returnValue = '';

        // Try using realpath()
        if (file_exists($pFilename)) {
            $returnValue = realpath($pFilename);
        }

        // Found something?
        if ($returnValue == '' || ($returnValue === null)) {
            $pathArray = explode('/', $pFilename);
            while (in_array('..', $pathArray) && $pathArray[0] != '..') {
                $iMax = count($pathArray);
                for ($i = 0; $i < $iMax; ++$i) {
                    if ($pathArray[$i] == '..' && $i > 0) {
                        unset($pathArray[$i], $pathArray[$i - 1]);

                        break;
                    }
                }
            }
            $returnValue = implode('/', $pathArray);
        }

        // Return
        return $returnValue;
    }

    /**
     * Get the systems temporary directory.
     *
     * @return string
     */
    public static function sysGetTempDir()
    {
        if (self::$useUploadTempDirectory) {
            //  use upload-directory when defined to allow running on environments having very restricted
            //      open_basedir configs
            if (ini_get('upload_tmp_dir') !== false) {
                if ($temp = ini_get('upload_tmp_dir')) {
                    if (file_exists($temp)) {
                        return realpath($temp);
                    }
                }
            }
        }

        return realpath(sys_get_temp_dir());
    }

    /**
     * Assert that given path is an existing file and is readable, otherwise throw exception.
     *
     * @param string $filename
     *
     * @throws InvalidArgumentException
     */
    public static function assertFile($filename)
    {
        if (!is_file($filename)) {
            throw new InvalidArgumentException('File "' . $filename . '" does not exist.');
        }

        if (!is_readable($filename)) {
            throw new InvalidArgumentException('Could not open "' . $filename . '" for reading.');
        }
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Writer;

use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\Run;
use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
use PhpOffice\PhpSpreadsheet\Shared\Escher;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
use PhpOffice\PhpSpreadsheet\Shared\OLE;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use RuntimeException;

class Xls extends BaseWriter
{
    /**
     * PhpSpreadsheet object.
     *
     * @var Spreadsheet
     */
    private $spreadsheet;

    /**
     * Total number of shared strings in workbook.
     *
     * @var int
     */
    private $strTotal = 0;

    /**
     * Number of unique shared strings in workbook.
     *
     * @var int
     */
    private $strUnique = 0;

    /**
     * Array of unique shared strings in workbook.
     *
     * @var array
     */
    private $strTable = [];

    /**
     * Color cache. Mapping between RGB value and color index.
     *
     * @var array
     */
    private $colors;

    /**
     * Formula parser.
     *
     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
     */
    private $parser;

    /**
     * Identifier clusters for drawings. Used in MSODRAWINGGROUP record.
     *
     * @var array
     */
    private $IDCLs;

    /**
     * Basic OLE object summary information.
     *
     * @var array
     */
    private $summaryInformation;

    /**
     * Extended OLE object document summary information.
     *
     * @var array
     */
    private $documentSummaryInformation;

    /**
     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook
     */
    private $writerWorkbook;

    /**
     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Worksheet[]
     */
    private $writerWorksheets;

    /**
     * Create a new Xls Writer.
     *
     * @param Spreadsheet $spreadsheet PhpSpreadsheet object
     */
    public function __construct(Spreadsheet $spreadsheet)
    {
        $this->spreadsheet = $spreadsheet;

        $this->parser = new Xls\Parser();
    }

    /**
     * Save Spreadsheet to file.
     *
     * @param string $pFilename
     *
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     */
    public function save($pFilename)
    {
        // garbage collect
        $this->spreadsheet->garbageCollect();

        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
        $saveDateReturnType = Functions::getReturnDateType();
        Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);

        // initialize colors array
        $this->colors = [];

        // Initialise workbook writer
        $this->writerWorkbook = new Xls\Workbook($this->spreadsheet, $this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser);

        // Initialise worksheet writers
        $countSheets = $this->spreadsheet->getSheetCount();
        for ($i = 0; $i < $countSheets; ++$i) {
            $this->writerWorksheets[$i] = new Xls\Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i));
        }

        // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook.
        $this->buildWorksheetEschers();
        $this->buildWorkbookEscher();

        // add 15 identical cell style Xfs
        // for now, we use the first cellXf instead of cellStyleXf
        $cellXfCollection = $this->spreadsheet->getCellXfCollection();
        for ($i = 0; $i < 15; ++$i) {
            $this->writerWorkbook->addXfWriter($cellXfCollection[0], true);
        }

        // add all the cell Xfs
        foreach ($this->spreadsheet->getCellXfCollection() as $style) {
            $this->writerWorkbook->addXfWriter($style, false);
        }

        // add fonts from rich text eleemnts
        for ($i = 0; $i < $countSheets; ++$i) {
            foreach ($this->writerWorksheets[$i]->phpSheet->getCoordinates() as $coordinate) {
                $cell = $this->writerWorksheets[$i]->phpSheet->getCell($coordinate);
                $cVal = $cell->getValue();
                if ($cVal instanceof RichText) {
                    $elements = $cVal->getRichTextElements();
                    foreach ($elements as $element) {
                        if ($element instanceof Run) {
                            $font = $element->getFont();
                            $this->writerWorksheets[$i]->fontHashIndex[$font->getHashCode()] = $this->writerWorkbook->addFont($font);
                        }
                    }
                }
            }
        }

        // initialize OLE file
        $workbookStreamName = 'Workbook';
        $OLE = new File(OLE::ascToUcs($workbookStreamName));

        // Write the worksheet streams before the global workbook stream,
        // because the byte sizes of these are needed in the global workbook stream
        $worksheetSizes = [];
        for ($i = 0; $i < $countSheets; ++$i) {
            $this->writerWorksheets[$i]->close();
            $worksheetSizes[] = $this->writerWorksheets[$i]->_datasize;
        }

        // add binary data for global workbook stream
        $OLE->append($this->writerWorkbook->writeWorkbook($worksheetSizes));

        // add binary data for sheet streams
        for ($i = 0; $i < $countSheets; ++$i) {
            $OLE->append($this->writerWorksheets[$i]->getData());
        }

        $this->documentSummaryInformation = $this->writeDocumentSummaryInformation();
        // initialize OLE Document Summary Information
        if (isset($this->documentSummaryInformation) && !empty($this->documentSummaryInformation)) {
            $OLE_DocumentSummaryInformation = new File(OLE::ascToUcs(chr(5) . 'DocumentSummaryInformation'));
            $OLE_DocumentSummaryInformation->append($this->documentSummaryInformation);
        }

        $this->summaryInformation = $this->writeSummaryInformation();
        // initialize OLE Summary Information
        if (isset($this->summaryInformation) && !empty($this->summaryInformation)) {
            $OLE_SummaryInformation = new File(OLE::ascToUcs(chr(5) . 'SummaryInformation'));
            $OLE_SummaryInformation->append($this->summaryInformation);
        }

        // define OLE Parts
        $arrRootData = [$OLE];
        // initialize OLE Properties file
        if (isset($OLE_SummaryInformation)) {
            $arrRootData[] = $OLE_SummaryInformation;
        }
        // initialize OLE Extended Properties file
        if (isset($OLE_DocumentSummaryInformation)) {
            $arrRootData[] = $OLE_DocumentSummaryInformation;
        }

        $root = new Root(time(), time(), $arrRootData);
        // save the OLE file
        $root->save($pFilename);

        Functions::setReturnDateType($saveDateReturnType);
        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
    }

    /**
     * Build the Worksheet Escher objects.
     */
    private function buildWorksheetEschers()
    {
        // 1-based index to BstoreContainer
        $blipIndex = 0;
        $lastReducedSpId = 0;
        $lastSpId = 0;

        foreach ($this->spreadsheet->getAllsheets() as $sheet) {
            // sheet index
            $sheetIndex = $sheet->getParent()->getIndex($sheet);

            $escher = null;

            // check if there are any shapes for this sheet
            $filterRange = $sheet->getAutoFilter()->getRange();
            if (count($sheet->getDrawingCollection()) == 0 && empty($filterRange)) {
                continue;
            }

            // create intermediate Escher object
            $escher = new Escher();

            // dgContainer
            $dgContainer = new DgContainer();

            // set the drawing index (we use sheet index + 1)
            $dgId = $sheet->getParent()->getIndex($sheet) + 1;
            $dgContainer->setDgId($dgId);
            $escher->setDgContainer($dgContainer);

            // spgrContainer
            $spgrContainer = new SpgrContainer();
            $dgContainer->setSpgrContainer($spgrContainer);

            // add one shape which is the group shape
            $spContainer = new SpContainer();
            $spContainer->setSpgr(true);
            $spContainer->setSpType(0);
            $spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10);
            $spgrContainer->addChild($spContainer);

            // add the shapes

            $countShapes[$sheetIndex] = 0; // count number of shapes (minus group shape), in sheet

            foreach ($sheet->getDrawingCollection() as $drawing) {
                ++$blipIndex;

                ++$countShapes[$sheetIndex];

                // add the shape
                $spContainer = new SpContainer();

                // set the shape type
                $spContainer->setSpType(0x004B);
                // set the shape flag
                $spContainer->setSpFlag(0x02);

                // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
                $reducedSpId = $countShapes[$sheetIndex];
                $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
                $spContainer->setSpId($spId);

                // keep track of last reducedSpId
                $lastReducedSpId = $reducedSpId;

                // keep track of last spId
                $lastSpId = $spId;

                // set the BLIP index
                $spContainer->setOPT(0x4104, $blipIndex);

                // set coordinates and offsets, client anchor
                $coordinates = $drawing->getCoordinates();
                $offsetX = $drawing->getOffsetX();
                $offsetY = $drawing->getOffsetY();
                $width = $drawing->getWidth();
                $height = $drawing->getHeight();

                $twoAnchor = \PhpOffice\PhpSpreadsheet\Shared\Xls::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height);

                $spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
                $spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
                $spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
                $spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
                $spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
                $spContainer->setEndOffsetY($twoAnchor['endOffsetY']);

                $spgrContainer->addChild($spContainer);
            }

            // AutoFilters
            if (!empty($filterRange)) {
                $rangeBounds = Coordinate::rangeBoundaries($filterRange);
                $iNumColStart = $rangeBounds[0][0];
                $iNumColEnd = $rangeBounds[1][0];

                $iInc = $iNumColStart;
                while ($iInc <= $iNumColEnd) {
                    ++$countShapes[$sheetIndex];

                    // create an Drawing Object for the dropdown
                    $oDrawing = new BaseDrawing();
                    // get the coordinates of drawing
                    $cDrawing = Coordinate::stringFromColumnIndex($iInc) . $rangeBounds[0][1];
                    $oDrawing->setCoordinates($cDrawing);
                    $oDrawing->setWorksheet($sheet);

                    // add the shape
                    $spContainer = new SpContainer();
                    // set the shape type
                    $spContainer->setSpType(0x00C9);
                    // set the shape flag
                    $spContainer->setSpFlag(0x01);

                    // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
                    $reducedSpId = $countShapes[$sheetIndex];
                    $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
                    $spContainer->setSpId($spId);

                    // keep track of last reducedSpId
                    $lastReducedSpId = $reducedSpId;

                    // keep track of last spId
                    $lastSpId = $spId;

                    $spContainer->setOPT(0x007F, 0x01040104); // Protection -> fLockAgainstGrouping
                    $spContainer->setOPT(0x00BF, 0x00080008); // Text -> fFitTextToShape
                    $spContainer->setOPT(0x01BF, 0x00010000); // Fill Style -> fNoFillHitTest
                    $spContainer->setOPT(0x01FF, 0x00080000); // Line Style -> fNoLineDrawDash
                    $spContainer->setOPT(0x03BF, 0x000A0000); // Group Shape -> fPrint

                    // set coordinates and offsets, client anchor
                    $endCoordinates = Coordinate::stringFromColumnIndex($iInc);
                    $endCoordinates .= $rangeBounds[0][1] + 1;

                    $spContainer->setStartCoordinates($cDrawing);
                    $spContainer->setStartOffsetX(0);
                    $spContainer->setStartOffsetY(0);
                    $spContainer->setEndCoordinates($endCoordinates);
                    $spContainer->setEndOffsetX(0);
                    $spContainer->setEndOffsetY(0);

                    $spgrContainer->addChild($spContainer);
                    ++$iInc;
                }
            }

            // identifier clusters, used for workbook Escher object
            $this->IDCLs[$dgId] = $lastReducedSpId;

            // set last shape index
            $dgContainer->setLastSpId($lastSpId);

            // set the Escher object
            $this->writerWorksheets[$sheetIndex]->setEscher($escher);
        }
    }

    /**
     * Build the Escher object corresponding to the MSODRAWINGGROUP record.
     */
    private function buildWorkbookEscher()
    {
        $escher = null;

        // any drawings in this workbook?
        $found = false;
        foreach ($this->spreadsheet->getAllSheets() as $sheet) {
            if (count($sheet->getDrawingCollection()) > 0) {
                $found = true;

                break;
            }
        }

        // nothing to do if there are no drawings
        if (!$found) {
            return;
        }

        // if we reach here, then there are drawings in the workbook
        $escher = new Escher();

        // dggContainer
        $dggContainer = new DggContainer();
        $escher->setDggContainer($dggContainer);

        // set IDCLs (identifier clusters)
        $dggContainer->setIDCLs($this->IDCLs);

        // this loop is for determining maximum shape identifier of all drawing
        $spIdMax = 0;
        $totalCountShapes = 0;
        $countDrawings = 0;

        foreach ($this->spreadsheet->getAllsheets() as $sheet) {
            $sheetCountShapes = 0; // count number of shapes (minus group shape), in sheet

            if (count($sheet->getDrawingCollection()) > 0) {
                ++$countDrawings;

                foreach ($sheet->getDrawingCollection() as $drawing) {
                    ++$sheetCountShapes;
                    ++$totalCountShapes;

                    $spId = $sheetCountShapes | ($this->spreadsheet->getIndex($sheet) + 1) << 10;
                    $spIdMax = max($spId, $spIdMax);
                }
            }
        }

        $dggContainer->setSpIdMax($spIdMax + 1);
        $dggContainer->setCDgSaved($countDrawings);
        $dggContainer->setCSpSaved($totalCountShapes + $countDrawings); // total number of shapes incl. one group shapes per drawing

        // bstoreContainer
        $bstoreContainer = new BstoreContainer();
        $dggContainer->setBstoreContainer($bstoreContainer);

        // the BSE's (all the images)
        foreach ($this->spreadsheet->getAllsheets() as $sheet) {
            foreach ($sheet->getDrawingCollection() as $drawing) {
                if (!extension_loaded('gd')) {
                    throw new RuntimeException('Saving images in xls requires gd extension');
                }
                if ($drawing instanceof Drawing) {
                    $filename = $drawing->getPath();

                    list($imagesx, $imagesy, $imageFormat) = getimagesize($filename);

                    switch ($imageFormat) {
                        case 1: // GIF, not supported by BIFF8, we convert to PNG
                            $blipType = BSE::BLIPTYPE_PNG;
                            ob_start();
                            imagepng(imagecreatefromgif($filename));
                            $blipData = ob_get_contents();
                            ob_end_clean();

                            break;
                        case 2: // JPEG
                            $blipType = BSE::BLIPTYPE_JPEG;
                            $blipData = file_get_contents($filename);

                            break;
                        case 3: // PNG
                            $blipType = BSE::BLIPTYPE_PNG;
                            $blipData = file_get_contents($filename);

                            break;
                        case 6: // Windows DIB (BMP), we convert to PNG
                            $blipType = BSE::BLIPTYPE_PNG;
                            ob_start();
                            imagepng(SharedDrawing::imagecreatefrombmp($filename));
                            $blipData = ob_get_contents();
                            ob_end_clean();

                            break;
                        default:
                            continue 2;
                    }

                    $blip = new Blip();
                    $blip->setData($blipData);

                    $BSE = new BSE();
                    $BSE->setBlipType($blipType);
                    $BSE->setBlip($blip);

                    $bstoreContainer->addBSE($BSE);
                } elseif ($drawing instanceof MemoryDrawing) {
                    switch ($drawing->getRenderingFunction()) {
                        case MemoryDrawing::RENDERING_JPEG:
                            $blipType = BSE::BLIPTYPE_JPEG;
                            $renderingFunction = 'imagejpeg';

                            break;
                        case MemoryDrawing::RENDERING_GIF:
                        case MemoryDrawing::RENDERING_PNG:
                        case MemoryDrawing::RENDERING_DEFAULT:
                            $blipType = BSE::BLIPTYPE_PNG;
                            $renderingFunction = 'imagepng';

                            break;
                    }

                    ob_start();
                    call_user_func($renderingFunction, $drawing->getImageResource());
                    $blipData = ob_get_contents();
                    ob_end_clean();

                    $blip = new Blip();
                    $blip->setData($blipData);

                    $BSE = new BSE();
                    $BSE->setBlipType($blipType);
                    $BSE->setBlip($blip);

                    $bstoreContainer->addBSE($BSE);
                }
            }
        }

        // Set the Escher object
        $this->writerWorkbook->setEscher($escher);
    }

    /**
     * Build the OLE Part for DocumentSummary Information.
     *
     * @return string
     */
    private function writeDocumentSummaryInformation()
    {
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
        $data = pack('v', 0xFFFE);
        // offset: 2; size: 2;
        $data .= pack('v', 0x0000);
        // offset: 4; size: 2; OS version
        $data .= pack('v', 0x0106);
        // offset: 6; size: 2; OS indicator
        $data .= pack('v', 0x0002);
        // offset: 8; size: 16
        $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
        // offset: 24; size: 4; section count
        $data .= pack('V', 0x0001);

        // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
        $data .= pack('vvvvvvvv', 0xD502, 0xD5CD, 0x2E9C, 0x101B, 0x9793, 0x0008, 0x2C2B, 0xAEF9);
        // offset: 44; size: 4; offset of the start
        $data .= pack('V', 0x30);

        // SECTION
        $dataSection = [];
        $dataSection_NumProps = 0;
        $dataSection_Summary = '';
        $dataSection_Content = '';

        // GKPIDDSI_CODEPAGE: CodePage
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x01],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x02], // 2 byte signed integer
            'data' => ['data' => 1252],
        ];
        ++$dataSection_NumProps;

        // GKPIDDSI_CATEGORY : Category
        if ($this->spreadsheet->getProperties()->getCategory()) {
            $dataProp = $this->spreadsheet->getProperties()->getCategory();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x02],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E],
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        // GKPIDDSI_VERSION :Version of the application that wrote the property storage
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x17],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x03],
            'data' => ['pack' => 'V', 'data' => 0x000C0000],
        ];
        ++$dataSection_NumProps;
        // GKPIDDSI_SCALE : FALSE
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x0B],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x0B],
            'data' => ['data' => false],
        ];
        ++$dataSection_NumProps;
        // GKPIDDSI_LINKSDIRTY : True if any of the values for the linked properties have changed outside of the application
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x10],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x0B],
            'data' => ['data' => false],
        ];
        ++$dataSection_NumProps;
        // GKPIDDSI_SHAREDOC : FALSE
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x13],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x0B],
            'data' => ['data' => false],
        ];
        ++$dataSection_NumProps;
        // GKPIDDSI_HYPERLINKSCHANGED : True if any of the values for the _PID_LINKS (hyperlink text) have changed outside of the application
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x16],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x0B],
            'data' => ['data' => false],
        ];
        ++$dataSection_NumProps;

        // GKPIDDSI_DOCSPARTS
        // MS-OSHARED p75 (2.3.3.2.2.1)
        // Structure is VtVecUnalignedLpstrValue (2.3.3.1.9)
        // cElements
        $dataProp = pack('v', 0x0001);
        $dataProp .= pack('v', 0x0000);
        // array of UnalignedLpstr
        // cch
        $dataProp .= pack('v', 0x000A);
        $dataProp .= pack('v', 0x0000);
        // value
        $dataProp .= 'Worksheet' . chr(0);

        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x0D],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x101E],
            'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
        ];
        ++$dataSection_NumProps;

        // GKPIDDSI_HEADINGPAIR
        // VtVecHeadingPairValue
        // cElements
        $dataProp = pack('v', 0x0002);
        $dataProp .= pack('v', 0x0000);
        // Array of vtHeadingPair
        // vtUnalignedString - headingString
        // stringType
        $dataProp .= pack('v', 0x001E);
        // padding
        $dataProp .= pack('v', 0x0000);
        // UnalignedLpstr
        // cch
        $dataProp .= pack('v', 0x0013);
        $dataProp .= pack('v', 0x0000);
        // value
        $dataProp .= 'Feuilles de calcul';
        // vtUnalignedString - headingParts
        // wType : 0x0003 = 32 bit signed integer
        $dataProp .= pack('v', 0x0300);
        // padding
        $dataProp .= pack('v', 0x0000);
        // value
        $dataProp .= pack('v', 0x0100);
        $dataProp .= pack('v', 0x0000);
        $dataProp .= pack('v', 0x0000);
        $dataProp .= pack('v', 0x0000);

        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x0C],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x100C],
            'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
        ];
        ++$dataSection_NumProps;

        //         4     Section Length
        //        4     Property count
        //        8 * $dataSection_NumProps (8 =  ID (4) + OffSet(4))
        $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
        foreach ($dataSection as $dataProp) {
            // Summary
            $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
            // Offset
            $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
            // DataType
            $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
            // Data
            if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer
                $dataSection_Content .= pack('V', $dataProp['data']['data']);

                $dataSection_Content_Offset += 4 + 4;
            } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer
                $dataSection_Content .= pack('V', $dataProp['data']['data']);

                $dataSection_Content_Offset += 4 + 4;
            } elseif ($dataProp['type']['data'] == 0x0B) { // Boolean
                if ($dataProp['data']['data'] == false) {
                    $dataSection_Content .= pack('V', 0x0000);
                } else {
                    $dataSection_Content .= pack('V', 0x0001);
                }
                $dataSection_Content_Offset += 4 + 4;
            } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length
                // Null-terminated string
                $dataProp['data']['data'] .= chr(0);
                $dataProp['data']['length'] += 1;
                // Complete the string with null string for being a %4
                $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4) == 4 ? 0 : (4 - $dataProp['data']['length'] % 4));
                $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);

                $dataSection_Content .= pack('V', $dataProp['data']['length']);
                $dataSection_Content .= $dataProp['data']['data'];

                $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
            } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
                $dataSection_Content .= $dataProp['data']['data'];

                $dataSection_Content_Offset += 4 + 8;
            } else {
                // Data Type Not Used at the moment
                $dataSection_Content .= $dataProp['data']['data'];

                $dataSection_Content_Offset += 4 + $dataProp['data']['length'];
            }
        }
        // Now $dataSection_Content_Offset contains the size of the content

        // section header
        // offset: $secOffset; size: 4; section length
        //         + x  Size of the content (summary + content)
        $data .= pack('V', $dataSection_Content_Offset);
        // offset: $secOffset+4; size: 4; property count
        $data .= pack('V', $dataSection_NumProps);
        // Section Summary
        $data .= $dataSection_Summary;
        // Section Content
        $data .= $dataSection_Content;

        return $data;
    }

    /**
     * Build the OLE Part for Summary Information.
     *
     * @return string
     */
    private function writeSummaryInformation()
    {
        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
        $data = pack('v', 0xFFFE);
        // offset: 2; size: 2;
        $data .= pack('v', 0x0000);
        // offset: 4; size: 2; OS version
        $data .= pack('v', 0x0106);
        // offset: 6; size: 2; OS indicator
        $data .= pack('v', 0x0002);
        // offset: 8; size: 16
        $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
        // offset: 24; size: 4; section count
        $data .= pack('V', 0x0001);

        // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
        $data .= pack('vvvvvvvv', 0x85E0, 0xF29F, 0x4FF9, 0x1068, 0x91AB, 0x0008, 0x272B, 0xD9B3);
        // offset: 44; size: 4; offset of the start
        $data .= pack('V', 0x30);

        // SECTION
        $dataSection = [];
        $dataSection_NumProps = 0;
        $dataSection_Summary = '';
        $dataSection_Content = '';

        // CodePage : CP-1252
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x01],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x02], // 2 byte signed integer
            'data' => ['data' => 1252],
        ];
        ++$dataSection_NumProps;

        //    Title
        if ($this->spreadsheet->getProperties()->getTitle()) {
            $dataProp = $this->spreadsheet->getProperties()->getTitle();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x02],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Subject
        if ($this->spreadsheet->getProperties()->getSubject()) {
            $dataProp = $this->spreadsheet->getProperties()->getSubject();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x03],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Author (Creator)
        if ($this->spreadsheet->getProperties()->getCreator()) {
            $dataProp = $this->spreadsheet->getProperties()->getCreator();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x04],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Keywords
        if ($this->spreadsheet->getProperties()->getKeywords()) {
            $dataProp = $this->spreadsheet->getProperties()->getKeywords();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x05],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Comments (Description)
        if ($this->spreadsheet->getProperties()->getDescription()) {
            $dataProp = $this->spreadsheet->getProperties()->getDescription();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x06],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Last Saved By (LastModifiedBy)
        if ($this->spreadsheet->getProperties()->getLastModifiedBy()) {
            $dataProp = $this->spreadsheet->getProperties()->getLastModifiedBy();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x08],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Created Date/Time
        if ($this->spreadsheet->getProperties()->getCreated()) {
            $dataProp = $this->spreadsheet->getProperties()->getCreated();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x0C],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x40], // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
                'data' => ['data' => OLE::localDateToOLE($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Modified Date/Time
        if ($this->spreadsheet->getProperties()->getModified()) {
            $dataProp = $this->spreadsheet->getProperties()->getModified();
            $dataSection[] = [
                'summary' => ['pack' => 'V', 'data' => 0x0D],
                'offset' => ['pack' => 'V'],
                'type' => ['pack' => 'V', 'data' => 0x40], // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
                'data' => ['data' => OLE::localDateToOLE($dataProp)],
            ];
            ++$dataSection_NumProps;
        }
        //    Security
        $dataSection[] = [
            'summary' => ['pack' => 'V', 'data' => 0x13],
            'offset' => ['pack' => 'V'],
            'type' => ['pack' => 'V', 'data' => 0x03], // 4 byte signed integer
            'data' => ['data' => 0x00],
        ];
        ++$dataSection_NumProps;

        //         4     Section Length
        //        4     Property count
        //        8 * $dataSection_NumProps (8 =  ID (4) + OffSet(4))
        $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
        foreach ($dataSection as $dataProp) {
            // Summary
            $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
            // Offset
            $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
            // DataType
            $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
            // Data
            if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer
                $dataSection_Content .= pack('V', $dataProp['data']['data']);

                $dataSection_Content_Offset += 4 + 4;
            } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer
                $dataSection_Content .= pack('V', $dataProp['data']['data']);

                $dataSection_Content_Offset += 4 + 4;
            } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length
                // Null-terminated string
                $dataProp['data']['data'] .= chr(0);
                $dataProp['data']['length'] += 1;
                // Complete the string with null string for being a %4
                $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4) == 4 ? 0 : (4 - $dataProp['data']['length'] % 4));
                $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);

                $dataSection_Content .= pack('V', $dataProp['data']['length']);
                $dataSection_Content .= $dataProp['data']['data'];

                $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
            } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
                $dataSection_Content .= $dataProp['data']['data'];

                $dataSection_Content_Offset += 4 + 8;
            }
            // Data Type Not Used at the moment
        }
        // Now $dataSection_Content_Offset contains the size of the content

        // section header
        // offset: $secOffset; size: 4; section length
        //         + x  Size of the content (summary + content)
        $data .= pack('V', $dataSection_Content_Offset);
        // offset: $secOffset+4; size: 4; property count
        $data .= pack('V', $dataSection_NumProps);
        // Section Summary
        $data .= $dataSection_Summary;
        // Section Content
        $data .= $dataSection_Content;

        return $data;
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Style;

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

class Style extends Supervisor
{
    /**
     * Font.
     *
     * @var Font
     */
    protected $font;

    /**
     * Fill.
     *
     * @var Fill
     */
    protected $fill;

    /**
     * Borders.
     *
     * @var Borders
     */
    protected $borders;

    /**
     * Alignment.
     *
     * @var Alignment
     */
    protected $alignment;

    /**
     * Number Format.
     *
     * @var NumberFormat
     */
    protected $numberFormat;

    /**
     * Conditional styles.
     *
     * @var Conditional[]
     */
    protected $conditionalStyles;

    /**
     * Protection.
     *
     * @var Protection
     */
    protected $protection;

    /**
     * Index of style in collection. Only used for real style.
     *
     * @var int
     */
    protected $index;

    /**
     * Use Quote Prefix when displaying in cell editor. Only used for real style.
     *
     * @var bool
     */
    protected $quotePrefix = false;

    /**
     * Create a new Style.
     *
     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
     *         Leave this value at default unless you understand exactly what
     *    its ramifications are
     * @param bool $isConditional Flag indicating if this is a conditional style or not
     *       Leave this value at default unless you understand exactly what
     *    its ramifications are
     */
    public function __construct($isSupervisor = false, $isConditional = false)
    {
        parent::__construct($isSupervisor);

        // Initialise values
        $this->conditionalStyles = [];
        $this->font = new Font($isSupervisor, $isConditional);
        $this->fill = new Fill($isSupervisor, $isConditional);
        $this->borders = new Borders($isSupervisor, $isConditional);
        $this->alignment = new Alignment($isSupervisor, $isConditional);
        $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
        $this->protection = new Protection($isSupervisor, $isConditional);

        // bind parent if we are a supervisor
        if ($isSupervisor) {
            $this->font->bindParent($this);
            $this->fill->bindParent($this);
            $this->borders->bindParent($this);
            $this->alignment->bindParent($this);
            $this->numberFormat->bindParent($this);
            $this->protection->bindParent($this);
        }
    }

    /**
     * Get the shared style component for the currently active cell in currently active sheet.
     * Only used for style supervisor.
     *
     * @return Style
     */
    public function getSharedComponent()
    {
        $activeSheet = $this->getActiveSheet();
        $selectedCell = $this->getActiveCell(); // e.g. 'A1'

        if ($activeSheet->cellExists($selectedCell)) {
            $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
        } else {
            $xfIndex = 0;
        }

        return $this->parent->getCellXfByIndex($xfIndex);
    }

    /**
     * Get parent. Only used for style supervisor.
     *
     * @return Spreadsheet
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Build style array from subcomponents.
     *
     * @param array $array
     *
     * @return array
     */
    public function getStyleArray($array)
    {
        return ['quotePrefix' => $array];
    }

    /**
     * Apply styles from array.
     *
     * <code>
     * $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray(
     *     [
     *         'font' => [
     *             'name' => 'Arial',
     *             'bold' => true,
     *             'italic' => false,
     *             'underline' => Font::UNDERLINE_DOUBLE,
     *             'strikethrough' => false,
     *             'color' => [
     *                 'rgb' => '808080'
     *             ]
     *         ],
     *         'borders' => [
     *             'bottom' => [
     *                 'borderStyle' => Border::BORDER_DASHDOT,
     *                 'color' => [
     *                     'rgb' => '808080'
     *                 ]
     *             ],
     *             'top' => [
     *                 'borderStyle' => Border::BORDER_DASHDOT,
     *                 'color' => [
     *                     'rgb' => '808080'
     *                 ]
     *             ]
     *         ],
     *         'quotePrefix'    => true
     *     ]
     * );
     * </code>
     *
     * @param array $pStyles Array containing style information
     * @param bool $pAdvanced advanced mode for setting borders
     *
     * @return Style
     */
    public function applyFromArray(array $pStyles, $pAdvanced = true)
    {
        if ($this->isSupervisor) {
            $pRange = $this->getSelectedCells();

            // Uppercase coordinate
            $pRange = strtoupper($pRange);

            // Is it a cell range or a single cell?
            if (strpos($pRange, ':') === false) {
                $rangeA = $pRange;
                $rangeB = $pRange;
            } else {
                list($rangeA, $rangeB) = explode(':', $pRange);
            }

            // Calculate range outer borders
            $rangeStart = Coordinate::coordinateFromString($rangeA);
            $rangeEnd = Coordinate::coordinateFromString($rangeB);

            // Translate column into index
            $rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]);
            $rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]);

            // Make sure we can loop upwards on rows and columns
            if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
                $tmp = $rangeStart;
                $rangeStart = $rangeEnd;
                $rangeEnd = $tmp;
            }

            // ADVANCED MODE:
            if ($pAdvanced && isset($pStyles['borders'])) {
                // 'allBorders' is a shorthand property for 'outline' and 'inside' and
                //        it applies to components that have not been set explicitly
                if (isset($pStyles['borders']['allBorders'])) {
                    foreach (['outline', 'inside'] as $component) {
                        if (!isset($pStyles['borders'][$component])) {
                            $pStyles['borders'][$component] = $pStyles['borders']['allBorders'];
                        }
                    }
                    unset($pStyles['borders']['allBorders']); // not needed any more
                }
                // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
                //        it applies to components that have not been set explicitly
                if (isset($pStyles['borders']['outline'])) {
                    foreach (['top', 'right', 'bottom', 'left'] as $component) {
                        if (!isset($pStyles['borders'][$component])) {
                            $pStyles['borders'][$component] = $pStyles['borders']['outline'];
                        }
                    }
                    unset($pStyles['borders']['outline']); // not needed any more
                }
                // 'inside' is a shorthand property for 'vertical' and 'horizontal'
                //        it applies to components that have not been set explicitly
                if (isset($pStyles['borders']['inside'])) {
                    foreach (['vertical', 'horizontal'] as $component) {
                        if (!isset($pStyles['borders'][$component])) {
                            $pStyles['borders'][$component] = $pStyles['borders']['inside'];
                        }
                    }
                    unset($pStyles['borders']['inside']); // not needed any more
                }
                // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
                $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
                $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);

                // loop through up to 3 x 3 = 9 regions
                for ($x = 1; $x <= $xMax; ++$x) {
                    // start column index for region
                    $colStart = ($x == 3) ?
                        Coordinate::stringFromColumnIndex($rangeEnd[0])
                            : Coordinate::stringFromColumnIndex($rangeStart[0] + $x - 1);
                    // end column index for region
                    $colEnd = ($x == 1) ?
                        Coordinate::stringFromColumnIndex($rangeStart[0])
                            : Coordinate::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);

                    for ($y = 1; $y <= $yMax; ++$y) {
                        // which edges are touching the region
                        $edges = [];
                        if ($x == 1) {
                            // are we at left edge
                            $edges[] = 'left';
                        }
                        if ($x == $xMax) {
                            // are we at right edge
                            $edges[] = 'right';
                        }
                        if ($y == 1) {
                            // are we at top edge?
                            $edges[] = 'top';
                        }
                        if ($y == $yMax) {
                            // are we at bottom edge?
                            $edges[] = 'bottom';
                        }

                        // start row index for region
                        $rowStart = ($y == 3) ?
                            $rangeEnd[1] : $rangeStart[1] + $y - 1;

                        // end row index for region
                        $rowEnd = ($y == 1) ?
                            $rangeStart[1] : $rangeEnd[1] - $yMax + $y;

                        // build range for region
                        $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;

                        // retrieve relevant style array for region
                        $regionStyles = $pStyles;
                        unset($regionStyles['borders']['inside']);

                        // what are the inner edges of the region when looking at the selection
                        $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);

                        // inner edges that are not touching the region should take the 'inside' border properties if they have been set
                        foreach ($innerEdges as $innerEdge) {
                            switch ($innerEdge) {
                                case 'top':
                                case 'bottom':
                                    // should pick up 'horizontal' border property if set
                                    if (isset($pStyles['borders']['horizontal'])) {
                                        $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
                                    } else {
                                        unset($regionStyles['borders'][$innerEdge]);
                                    }

                                    break;
                                case 'left':
                                case 'right':
                                    // should pick up 'vertical' border property if set
                                    if (isset($pStyles['borders']['vertical'])) {
                                        $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
                                    } else {
                                        unset($regionStyles['borders'][$innerEdge]);
                                    }

                                    break;
                            }
                        }

                        // apply region style to region by calling applyFromArray() in simple mode
                        $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
                    }
                }

                return $this;
            }

            // SIMPLE MODE:
            // Selection type, inspect
            if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
                $selectionType = 'COLUMN';
            } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
                $selectionType = 'ROW';
            } else {
                $selectionType = 'CELL';
            }

            // First loop through columns, rows, or cells to find out which styles are affected by this operation
            switch ($selectionType) {
                case 'COLUMN':
                    $oldXfIndexes = [];
                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
                        $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
                    }

                    break;
                case 'ROW':
                    $oldXfIndexes = [];
                    for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
                        if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
                            $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
                        } else {
                            $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
                        }
                    }

                    break;
                case 'CELL':
                    $oldXfIndexes = [];
                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
                        for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
                            $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
                        }
                    }

                    break;
            }

            // clone each of the affected styles, apply the style array, and add the new styles to the workbook
            $workbook = $this->getActiveSheet()->getParent();
            foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
                $style = $workbook->getCellXfByIndex($oldXfIndex);
                $newStyle = clone $style;
                $newStyle->applyFromArray($pStyles);

                if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
                    // there is already such cell Xf in our collection
                    $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
                } else {
                    // we don't have such a cell Xf, need to add
                    $workbook->addCellXf($newStyle);
                    $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
                }
            }

            // Loop through columns, rows, or cells again and update the XF index
            switch ($selectionType) {
                case 'COLUMN':
                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
                        $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
                        $oldXfIndex = $columnDimension->getXfIndex();
                        $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
                    }

                    break;
                case 'ROW':
                    for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
                        $rowDimension = $this->getActiveSheet()->getRowDimension($row);
                        $oldXfIndex = $rowDimension->getXfIndex() === null ?
                            0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
                        $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
                    }

                    break;
                case 'CELL':
                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
                        for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
                            $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
                            $oldXfIndex = $cell->getXfIndex();
                            $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
                        }
                    }

                    break;
            }
        } else {
            // not a supervisor, just apply the style array directly on style object
            if (isset($pStyles['fill'])) {
                $this->getFill()->applyFromArray($pStyles['fill']);
            }
            if (isset($pStyles['font'])) {
                $this->getFont()->applyFromArray($pStyles['font']);
            }
            if (isset($pStyles['borders'])) {
                $this->getBorders()->applyFromArray($pStyles['borders']);
            }
            if (isset($pStyles['alignment'])) {
                $this->getAlignment()->applyFromArray($pStyles['alignment']);
            }
            if (isset($pStyles['numberFormat'])) {
                $this->getNumberFormat()->applyFromArray($pStyles['numberFormat']);
            }
            if (isset($pStyles['protection'])) {
                $this->getProtection()->applyFromArray($pStyles['protection']);
            }
            if (isset($pStyles['quotePrefix'])) {
                $this->quotePrefix = $pStyles['quotePrefix'];
            }
        }

        return $this;
    }

    /**
     * Get Fill.
     *
     * @return Fill
     */
    public function getFill()
    {
        return $this->fill;
    }

    /**
     * Get Font.
     *
     * @return Font
     */
    public function getFont()
    {
        return $this->font;
    }

    /**
     * Set font.
     *
     * @param Font $font
     *
     * @return Style
     */
    public function setFont(Font $font)
    {
        $this->font = $font;

        return $this;
    }

    /**
     * Get Borders.
     *
     * @return Borders
     */
    public function getBorders()
    {
        return $this->borders;
    }

    /**
     * Get Alignment.
     *
     * @return Alignment
     */
    public function getAlignment()
    {
        return $this->alignment;
    }

    /**
     * Get Number Format.
     *
     * @return NumberFormat
     */
    public function getNumberFormat()
    {
        return $this->numberFormat;
    }

    /**
     * Get Conditional Styles. Only used on supervisor.
     *
     * @return Conditional[]
     */
    public function getConditionalStyles()
    {
        return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
    }

    /**
     * Set Conditional Styles. Only used on supervisor.
     *
     * @param Conditional[] $pValue Array of conditional styles
     *
     * @return Style
     */
    public function setConditionalStyles(array $pValue)
    {
        $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);

        return $this;
    }

    /**
     * Get Protection.
     *
     * @return Protection
     */
    public function getProtection()
    {
        return $this->protection;
    }

    /**
     * Get quote prefix.
     *
     * @return bool
     */
    public function getQuotePrefix()
    {
        if ($this->isSupervisor) {
            return $this->getSharedComponent()->getQuotePrefix();
        }

        return $this->quotePrefix;
    }

    /**
     * Set quote prefix.
     *
     * @param bool $pValue
     *
     * @return Style
     */
    public function setQuotePrefix($pValue)
    {
        if ($pValue == '') {
            $pValue = false;
        }
        if ($this->isSupervisor) {
            $styleArray = ['quotePrefix' => $pValue];
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
        } else {
            $this->quotePrefix = (bool) $pValue;
        }

        return $this;
    }

    /**
     * Get hash code.
     *
     * @return string Hash code
     */
    public function getHashCode()
    {
        $hashConditionals = '';
        foreach ($this->conditionalStyles as $conditional) {
            $hashConditionals .= $conditional->getHashCode();
        }

        return md5(
            $this->fill->getHashCode() .
            $this->font->getHashCode() .
            $this->borders->getHashCode() .
            $this->alignment->getHashCode() .
            $this->numberFormat->getHashCode() .
            $hashConditionals .
            $this->protection->getHashCode() .
            ($this->quotePrefix ? 't' : 'f') .
            __CLASS__
        );
    }

    /**
     * Get own index in style collection.
     *
     * @return int
     */
    public function getIndex()
    {
        return $this->index;
    }

    /**
     * Set own index in style collection.
     *
     * @param int $pValue
     */
    public function setIndex($pValue)
    {
        $this->index = $pValue;
    }
}


<?php
namespace Katar;

/**
 * Receives an array of tokens and translates them onto PHP
 *
 * The tokenizer is optional and can be plugged in using setTokenizer,
 * when this happens the compile method can compile from a string instead
 * of an array of tokens.
 */
class Parser
{
    private $tokenizer;
    private $tokens;
    private $currLine;

    public function __construct() {
        $this->tokenizer = null;
        $this->tokens = array();
    }

    public function setTokenizer($tokenizer) {
        $this->tokenizer = $tokenizer;
    }

    public function compile($tokens) {
        $this->currLine = 0;

        // if we get an string, tokenize first
        if(is_string($tokens)) {
            if(!$this->tokenizer) {
                throw new \Exception("Tokenizer not found.");
            }

            $tokens = $this->tokenizer->tokenize($tokens);
        }

        $this->tokens = $tokens;

        // parse tokens! let each parser consume as much as wanted
        // as the token array is passed by reference
        $output = '';
        while(!empty($this->tokens)) {
            $output .= $this->parseExpression();
        }

        return $output;
    }

    /**
     * Pops the first element from the array, if $expected is specified it
     * also checks for a match, throwing an exception if the matching fails.
     *
     * @param array $array The token array
     * @param string $expected The expected type of the token
     *
     * @return array The first token of the array/queue
     */
    private function pop($expected = null) {
        if(count($this->tokens) == 0) {
            $message = is_null($expected)
                ? "Ran out of tokens, expected $expected."
                : 'Ran out of tokens.';

            throw new SyntaxErrorException($message);
        }

        $token = array_shift($this->tokens);
        $type = $token[0];

        if(!is_null($expected) && $type != $expected) {
            throw new SyntaxErrorException("Invalid token: Got $type, expected
                $expected.");
        }

        return $token;
    }

    /**
     * Peeks at the first element in the array/stack
     *
     * @param array The token array
     *
     * @return array The first token in the array/stack without removing it
     */
    private function peek() {
        if(count($this->tokens) == 0) {
            throw new \Exception("Unexpected end of input at line
                $this->currLine .");
        }

        return $this->tokens[0];
    }

    /**
     * Parses an IF expression
     */
    private function parseIf() {
        // consume required tokens
        $if_open = $this->pop('IF_OPEN');
        $output = 'if(' . $if_open[1] . ') {' . "\n";
        $this->currLine++;

        $seeking = true;
        while($seeking) {
            list($type, $value) = $this->peek();

            switch($type) {
            case 'IF_CLOSE':
                $this->pop();
                $output .= "}\n";
                $seeking = false;
                $this->currLine++;
                break;
            case 'ELSE':
                $this->pop();
                $output .= "} else {\n";
                $this->currLine++;
                break;
            case 'ELSE_IF':
                $token = $this->pop();
                $output .= '} elseif(' . $token[1] . ") {\n";
                $this->currLine++;
                break;
            default:
                $output .= $this->parseExpression();
                break;
            }
        }

        return $output;
    }

    /**
     * Parses an EXPRESSION
     *
     * Expression: (IF_OPEN | FOR_OPEN | FILTERED_VALUE | VALUE | HTML
     *  | ESCAPE)
     */
    public function parseExpression() {
        $token = $this->peek();
        // check first token
        $type = $token[0];

        switch($type) {
        case 'IF_OPEN':
            return $this->parseIf();
        case 'FOR_OPEN':
            return $this->parseFor();
        case 'FILTERED_VALUE':
            return $this->parseFilteredValue();
        case 'VALUE':
            return $this->parseValue();
        case 'HTML':
            return $this->parseHTML();
        case 'ESCAPE':
            return $this->parseEscape();
        case 'INCLUDE':
            return $this->parseInclude();
        default:
            throw new SyntaxErrorException(
                "Could not parse expression, invalid token '$type'");
        }
    }

    /**
     * Parses an HTML Expression
     */
    public function parseHTML() {
        $token = $this->pop('HTML');
        $value = $this->stripQuotes($token[1]);
        $this->currLine += substr_count($value, "\n");
        return '$output .= \'' . $value . "';\n";
    }

    /**
     * Parses a VALUE expression
     */
    public function parseValue() {
        $token = $this->pop('VALUE');
        return '$output .= ' . $token[1] . ";\n";
    }

    /**
     * Parses a FOR expression
     */
    public function parseFor() {
        // consume required tokens
        $for_open_token = $this->pop('FOR_OPEN');
        $this->currLine++;

        // create output so far
        $output = '$for_index = 0; foreach(' .
            $for_open_token[1][1] . ' as ' .
            $for_open_token[1][0] . ') {' . "\n";

        while(true) {
            list($type, $value) = $this->peek();

            if($type == 'FOR_CLOSE') {
                // pop the element, and add the value
                $this->pop();
                $output .= '$for_index++; }' . "\n";
                $this->currLine++;
                break;
            } else {
                $output .= $this->parseExpression();
            }
        }

        return $output;
    }

    /**
     * Parses an ESCAPE expression
     */
    public function parseEscape() {
        $token = $this->pop('ESCAPE');
        $value = $this->stripQuotes($token[1]);
        $this->currLine += substr_count($value, "\n");
        return '$output .= \'' . $value . "';\n";
    }

    /**
     * Parses a FILTERED_VALUE expression
     */
    public function parseFilteredValue() {
        list($type, $filters) = $this->pop('FILTERED_VALUE');
        $value = array_shift($filters);
        $opening = '';
        $closing = '';

        foreach($filters as $filter) {
            if(function_exists($filter)) {
                $opening .= $filter . '(';
                $closing .= ')';
            } else {
                $opening .= '\Katar\Katar::getInstance()->filter(\'' .
                    $filter . '\', ';
                $closing .= ')';
            }
        }

        return '$output .= ' . $opening . $value . $closing . ";\n";
    }

    public function parseInclude() {
        $token = $this->pop('INCLUDE');
        $file = $token[1];
        $this->currLine++;

        // this will later be replaced by Katar to the evaluated
        // result of the compiled $file
        // pretty much like a compiler directive
        return "\$output .= \Katar\Katar::getInstance()->render($file,"
           . " \$args);\n";
    }

    private function stripQuotes($str) {
        return str_replace(array(
            '"',
            "'",
        ), array(
            '"',
            '''
        ), $str);
    }
}

class SyntaxErrorException extends \Exception {};
class InvalidFilterException extends \Exception {};