<?php
//
//	guiedit - PukiWiki Plugin
//
//	License:
//	  GNU General Public License Version 2 or later (GPL)
//	  http://www.gnu.org/licenses/gpl.html
//
//	Copyright (C) 2006-2008 garand
//	PukiWiki : Copyright (C) 2001-2006 PukiWiki Developers Team
//	FCKeditor : Copyright (C) 2003-2008 Frederico Caldeira Knabben
//      PukiWiki Plus! : Copyright (C) 2009-2010 Katsumi Saito
//  PukiWiki : Copyright (C) 2003-2020 PukiWiki Developers Team
//	CKEditor : Copyright (C) 2003-2020 CKSource sp. z o.o. sp.k. All rights reserved.
//
//
//	File:
//	  htmlv2wiki.php
//	  HTML5 を PukiWiki の構文に変換
// PHP8対応、細かいところを修正 byはいふん

function htmlv2wiki($source)
{
    // 変換クラスのオブジェクト生成とその設定
    $obj = new HTMLV2Wiki();
    
    // 変換メソッドの呼び出し
    $body = $obj->Convert($source);
    
    return $body;
}


// 変換クラス
class HTMLV2Wiki
{
    public $body;
    public $text;
    public $parent_div;
    public $div_level;
    public $level_array;
    public $protect_data;
    public $multilineplugin;
    //	初期化
    public function __construct()
    {
        $this->parent_div = array('');
        $this->div_level = 0;
        $this->level_array = array(0);
        $this->protect_data = array();
        $this->text = '';
        $this->body = array();
        $this -> multilineplugin = false;
    }
    
    public function HTMLV2Wiki()
    {
        $this->__construct();
    }
    
    // 変換メソッド
    public function Convert($source)
    {
        $body2 = '';
        // <br /> が行末にならない場合の対処
        // -文字列~ のような場合の挙動に対応
        $source = preg_replace("/<br\s\/>\</", "~\n<", $source);
        $source = preg_replace("/^<div id=\"contents\">\n+?<div id=\"body\">(.*?)<\/div>\n+?<\/div>\n+?$/s", "$1", $source);

        // １行ずつに分割
        $source = explode("\n", $source);
        // 一行ずつ取り出し
        foreach ($source as $line) {
            $this->Div($line);
        }
        // 構文を結合
        $body2 = implode($this->body);

        // 構文補正
        //$body = preg_replace("/___GUIPD(\d+)___/e", '$this->protect_data["$1"-1]', $body);
        $body2 = preg_replace("/\n\n\n+/", "\n\n", $body2);
        $body2 = str_replace("___easyedit_special_string_deleted___", "", $body2);

        $body2 = preg_replace("/\/\/(.*?)~/", '//$1', $body2);


        // タブの除去
        $body2 = preg_replace('/'. "\t" .'+(.*?)/', '$1', $body2);

        return $body2;
    }

    // ブロック要素
    public function Div($line)
    {
        if ($this -> multilineplugin == true) {
            if (preg_match('/^\}{2,}<\/span>(<\/p>)?$/', $line)) {
                $this -> multilineplugin = false;
            } else {
                $line = str_replace("<br />", "___easyedit_special_string_deleted___", $line);
                $line = strip_tags($line);
                $this->OutputLine($this->DecodeSpecialChars($line));
                return;
            }
        }
        // 整形済みテキスト
        if (preg_match("/<pre>/", $line, $matches)) {
            $this->StartDiv('Pre');
        } elseif ($this->GetDiv() == 'Pre') {
            if (preg_match("/(.*)<\/pre>/", $line, $matches)) {
                $line = $matches[1];
                $this->EndDiv();
            }
            $line = preg_replace("/<br\s\/>/", "\n ", $line);
            $line = strip_tags($line);
            $this->OutputLine(' ' . $this->DecodeSpecialChars($line));
            return;
        }
        if ($line == '') {
            return;
        }

        // 直接埋込プラグイン
        if (preg_match("/<span\s[^>]*?class=\"embededplugin\".*?>.*?<\/span>/", $line)) {
            $line = preg_replace_callback("/<span\s([^>]*?class=\"embededplugin\"[^>]*?)>(.*?)<\/span>/", function ($matches) {
                if (preg_match("/_pkwkargs=\"([^\"]*?)\"/", $matches[1], $matches2)) {
                    $args = $matches2[1];
                }
                if (preg_match("/_type=\"([^\"]*?)\"/", $matches[1], $matches2)) {
                    $type = $matches2[1];
                }
                if (preg_match("/_plugin=\"([^\"]*?)\"/", $matches[1], $matches2)) {
                    $plugin = $matches2[1];
                }
                if ($plugin == "ref") {
                    if (preg_match("/<img\s([^>]*?)\s\/>/", $matches[2], $matches2)) {
                        if (preg_match("/src=\"([^\"]*?)\"/", $matches2[1], $matches3)) {
                            $src = $matches3[1];
                        }
                        if (preg_match("/style=\"([^\"]*?)\"/", $matches2[1], $matches3)) {
                            $style = $matches3[1];
                        }
                        $args_array = explode(',', $args);
                        $args = str_replace($args_array[0], "___src___", $args);
                        if (!empty($style) || $style != '') {
                            preg_match("/width\s?:\s?([^\s]*?)\s?;/", $style, $size_matches);
                            $size_w = $size_matches[1];
                            preg_match("/height\s?:\s?([^\s]*?)\s?;/", $style, $size_matches);
                            $size_h = $size_matches[1];
                            //echo $size_h;
                            if (substr($size_w, -1) == "%") {
                                $size = $size_w;
                            } else {
                                $size = str_replace("px", "", $size_w) . "x" . str_replace("px", "", $size_h);
                            }
                            if (preg_match("/([0-9]+)x([0-9]+)/", $args)) {
                                $args = preg_replace("/([0-9]+)x([0-9]+)/", $size, $args);
                            } elseif (preg_match("/^([0-9.]+)%$/", $args)) {
                                $args = preg_replace("/^([0-9.]+)%$/", $size, $args);
                            } else {
                                $args = $args . ',' . $size;
                            }
                            $args = str_replace("___src___", $src, $args);
                            unset($args_array);
                        }
                    }
                } elseif ($plugin == "submit" || $plugin == "button") {
                    return "<span class=\"plugin\">&amp;" . $plugin . "{" .$args . "};</span>";
                }
                return "<span class=\"plugin\">&amp;" . $plugin . "(" . $args . ");</span>";
            }, $line);
        } elseif (preg_match("/<div\s[^>]*?class=\"embededplugin\".*?>.*?<\/div>/", $line)) {
            $line = preg_replace_callback("/<div\s([^>]*?class=\"embededplugin\"[^>]*?)>(.*?)<\/div>/", function ($matches) {
                if (preg_match("/_pkwkargs=\"([^\"]*?)\"/", $matches[1], $matches2)) {
                    $args = $matches2[1];
                }
                if (preg_match("/_type=\"([^\"]*?)\"/", $matches[1], $matches2)) {
                    $type = $matches2[1];
                }
                if (preg_match("/_plugin=\"([^\"]*?)\"/", $matches[1], $matches2)) {
                    $plugin = $matches2[1];
                }
                if ($plugin == "ref") {
                    if (preg_match("/<img\s([^>]*?)\s\/>/", $matches[2], $matches2)) {
                        if (preg_match("/src=\"([^\"]*?)\"/", $matches2[1], $matches3)) {
                            $src = $matches3[1];
                        }
                        if (preg_match("/style=\"([^\"]*?)\"/", $matches2[1], $matches3)) {
                            $style = $matches3[1];
                        }
                        $args_array = explode(',', $args);
                        $args = str_replace($args_array[0], "___src___", $args);
                        if (!empty($style) || $style != '') {
                            preg_match("/width\s?:\s?([^\s]*?)\s?;/", $style, $size_matches);
                            $size_w = $size_matches[1];
                            preg_match("/height\s?:\s?([^\s]*?)\s?;/", $style, $size_matches);
                            $size_h = $size_matches[1];
                            //echo $size_h;
                            if (substr($size_w, -1) == "%") {
                                $size = $size_w;
                            } else {
                                $size = str_replace("px", "", $size_w) . "x" . str_replace("px", "", $size_h);
                            }
                            if (preg_match("/([0-9]+)x([0-9]+)/", $args)) {
                                $args = preg_replace("/([0-9]+)x([0-9]+)/", $size, $args);
                            } elseif (preg_match("/^([0-9.]+)%$/", $args)) {
                                $args = preg_replace("/^([0-9.]+)%$/", $size, $args);
                            } else {
                                $args = $args . ',' . $size;
                            }
                            $args = str_replace("___src___", $src, $args);
                            unset($args_array);
                        }
                    }
                }
                return "<div class=\"plugin\">#" . $plugin . "(" .$args . ")</div>";
            }, $line);
        }
        if ($this->GetDiv() == 'Table') {
            $this->Table($line);
            return;
        }

        // 見出し
        if (preg_match("/<h([2-4])(.*?)>(.*)/", $line, $matches)) {
            $this->StartDiv('Heading');
            $level = $matches[1];
            $line = $matches[3];
            $attribute = $matches[2];
            if (preg_match("/id=\"(\w+)\"/", $attribute, $matches)) {
                $line .= ' [#' . $matches[1] . ']';
            }
            $this->OutputLine(str_repeat("*", --$level) . " ", $line);
            $this->EndDiv();
        }
        // ブロック型プラグイン
        elseif (preg_match("/<span\s([^>]*?)class=\"(plugin|ref)\"(.*?)>(\#.*)/", $line)) {
            // 複数行プラグイン
            if (!PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK && preg_match('/\{{2,}(<br\s\/>)?$/', $line)) {
                $this -> multilineplugin = true;
            }
            $line = preg_replace("/<br\s\/>/", "\n", $line);
            $line = strip_tags($line);
            $this->OutputLine($this->DecodeSpecialChars($line));
        }
        // 引用文
        elseif (preg_match("/<blockquote.*?>/", $line)) {
            if ($this->GetDiv() != 'Blockquote') {
                $this->StartDiv('Blockquote');
            }
            $this->div_level++;
            $this->OutputLine(str_repeat('>', $this->GetLevel()));
        }
        // リスト
        elseif (preg_match("/<(o|u|d)l.*?>/", $line, $matches)) {
            $element = strtoupper($matches[1]) . 'List';
            if ($this->GetDiv() != $element) {
                $this->StartDiv($element);
            }
            $this->div_level++;
        }
        // テーブル
        elseif (preg_match("/<table.*?>/", $line)) {
            $this->StartDiv('Table');
        }
        // 水平線
        elseif (preg_match("/^<hr(\sclass=\"(full_hr|short_line)\")?\s\/>$/", $line, $matches)) {
            if ($matches[2] == 'short_line') {
                $this->OutputLine('#hr');
            } else {
                $this->OutputLine('----');
            }
        }
        // 改頁
        elseif (strpos($line, '<div style="page-break-after: always;">') !== false) {
            $this->OutputLine('#pagebreak');
        } else {
            switch ($this->GetDiv()) {
                case 'OList':
                    $this->OList($line);
                    break;
                case 'UList':
                    $this->UList($line);
                    break;
                case 'DList':
                    $this->DList($line);
                    break;
                case 'Blockquote':
                    $this->Blockquote($line);
                    break;
                default:
                    $this->Paragraph($line);
                    break;
            }
        }
    }

    // 番号付きリスト
    public function OList($line)
    {
        if (preg_match("/<".preg_quote("/", "/")."ol>/", $line)) {
            $this->div_level--;
            if ($this->div_level == 0) {
                $this->EndDiv();
                if ($this->GetDiv() == '') {
                    $this->OutputLine();
                }
            }
        } elseif (preg_match("/^(<li>)?(.*?)(<".preg_quote("/", "/")."li>)?$/", $line, $matches)) {
            $head = '';
            if ($matches[0]) {
                $head = str_repeat("+", $this->GetLevel()) . " ";
                $line1 = str_replace("<li>", "", $matches[0]);
                $line1 = str_replace("</li>", "", $line1);
            }
            if (!$matches[0] && isset($matches[2]) && !$matches[2]) {
                $this->Paragraph($line);
            } elseif ($head || $matches[0]) {
                $this->OutputLine($head, $matches[2]);
                //$this->OutputLine($head.$line1,$matches[2]);
            }
        }
    }
    
    // 番号なしリスト
    public function UList($line)
    {
        if (preg_match("/<".preg_quote("/", "/")."ul>/", $line)) {
            $this->div_level--;
            if ($this->div_level == 0) {
                $this->EndDiv();
                if ($this->GetDiv() == '') {
                    $this->OutputLine();
                }
            }
        } elseif (preg_match("/^(<li>)?(.*?)(<".preg_quote("/", "/")."li>)?$/", $line, $matches)) {
            $head = '';
            if ($matches[0]) {
                $head = str_repeat("-", $this->GetLevel()) . " ";
                $line1 = str_replace("<li>", "", $matches[0]);
                $line1 = str_replace("</li>", "", $line1);
            }
            if (!$matches[1] && isset($matches[2]) && !$matches[2]) {
                $this->Paragraph($line);
            } elseif ($head || $matches[0]) {
                //$this->OutputLine($head.$line1, $matches[2]);
                $this->OutputLine($head, $matches[2]);
            }
        }
    }
    
    // 定義リスト
    public function DList($line)
    {
        if (preg_match("/<\/dl>/", $line)) {
            $this->div_level--;
            if ($this->div_level == 0) {
                $this->EndDiv();
                if ($this->GetDiv() == '') {
                    $this->OutputLine();
                }
            }
        } elseif (preg_match("/^\s*(<d(t|d)>)?(.*?)(<\/d(t|d)>)?\s*$/", $line, $matches)) {
            $text = $matches[3];
            $head = str_repeat(":", $this->GetLevel()) . " ";
            $text = preg_replace("/<d(t|d)>/", "", $text);
            $text = preg_replace("/<\/d(t|d)>/", "", $text);
            if ($matches[2] == 't') {
                $this->OutputLine($head, $text, '|');
            } elseif ($text) {
                $this->OutputLine('', $text);
            }
        }
    }
    
    // 引用文
    public function Blockquote($line)
    {
        if (preg_match("/<\/blockquote>/", $line)) {
            if ($this->div_level <= 3) {
                $this->OutputLine(str_repeat('<', $this->GetLevel()) . " ", '');
            }
            $this->div_level--;
            if ($this->div_level == 0) {
                $this->EndDiv();
            }
        } elseif (preg_match("/(<p.*?>)(.*?)(<\/p>)$/", $line, $matches)) {
            if ($matches[2]) {
                $head = str_repeat('>', $this->div_level);
                $this->OutputLine($head, $matches[2]);
            }
        } elseif (preg_match("/(<p.*?>)?(.*?)(<\/p>)?$/", $line, $matches)) {
            if (!$matches[1] && !$matches[3]) {
                $this->Paragraph($line);
            } elseif ($matches[2]) {
                $head = $matches[1] ? str_repeat('>', $this->div_level) : '';
                $this->OutputLine($head, $matches[2]);
            }
        }
    }
    
    // テーブル
    public function Table($line)
    {
        static $cells;
        static $text;
        static $head, $hspace, $fspace;
        static $row, $col;
        static $is_cell = false;
        static $type = '';
        static $delm = '|';
        static $text_colgroups;
        static $colgroup_matching;

        // セルの開始
        if (preg_match("/<t(d|h)(\s[^>]*?)?>(.*)/", $line, $matches)) {
            $is_cell = true;
            $cell_type = $matches[1]; // d, h
            $attribute = $matches[2];
            $text = '';
            $line = $matches[3];
            $rowspan = 1;
            $colspan = 1;
            $hspace = 0;
            $fspace = 0;
            
            for (; !empty($cells[$row][$col]); $col++);
            
            // セルの連結
            if (preg_match("/rowspan=\"(\d+)\"/", $attribute, $matches)) {
                $rowspan = $matches[1];
            }
            if (preg_match("/colspan=\"(\d+)\"/", $attribute, $matches)) {
                $colspan = $matches[1];
            }
            for ($i = 1; $i < $rowspan; $i++) {
                for ($j = 0; $j < $colspan; $j++) {
                    $cells[$row + $i][$col + $j] = '~';
                }
            }
            for ($i = 1; $i < $colspan; $i++) {
                $cells[$row][$col++] = '>';
            }

            // セルの属性
            
            $head = $this->GetTableAttribute($attribute);
            // 空白
            if (preg_match("/_hspace=\"(\d+)\"/", $attribute, $matches)) {
                $hspace = $matches[1];
            }
            if (preg_match("/_fspace=\"(\d+)\"/", $attribute, $matches)) {
                $fspace = $matches[1];
            }
            // ヘッダセル
            $head .= ($cell_type == 'h') ? '~' : '';
        }
        
        // セル
        if ($is_cell) {
            if (preg_match("/(.*)<\/t(d|h)>/", $line, $matches)) {
                $text .= $matches[1];
                
                $text = $this->Inline($text);
                if (($cell_type == 'h' && $text == '' && $hspace == 0) ||
                    (preg_match('/^(?:LEFT|CENTER|RIGHT|(BG)?COLOR\([#\w]+\)|SIZE\(\d+\)):/', $text) && $hspace == 0)) {
                    $head .= ' ';
                }
                
                $cells[$row][$col] = $head . str_repeat(' ', $hspace) . $text . str_repeat(' ', $fspace);
                $col++;
                $is_cell = false;
            } else {
                $text .= $line;
            }
        }
        // 行の開始
        // UPK
        elseif (preg_match("/<tr.*?>/", $line)) {
            $col = 1;
            $row++;
            $delm = preg_match("/<tr>/", $line) ? '|' : ',';
        }
        // テーブルの終了
        elseif (preg_match("/<\/table>/", $line)) {
            $cells = null;
            $this->EndDiv();
            $this->body[] = "\n";
        }
        // 書式設定行の開始 HTML5への対応
        elseif (preg_match("/<colgroup>/", $line)) {
            $cells = array();
            $row = 0;
            $text_colgroups = '';
            $colgroup_matching = true;
        }
        // 書式設定行の属性取得 HTML5への対応
        elseif (preg_match("/<col\s([^\/]*?)\/>(.*)/", $line, $matches) && $colgroup_matching == true) {
            $line = $matches[2];
            $text_colgroups .= '|' . $this->GetTableAttribute($matches[1]);
        }
        // 書式設定行の終了 HTML5への対応
        elseif (preg_match("/<\/colgroup>/", $line)) {
            $this->body[] = $text_colgroups . "|c\n";
            $colgroup_matching = false;
            $cells = array();
        }
        // 書式設定行 FCKeditor
        /*else if (preg_match("/<colgroup>/", $line)) {
            if (count($cells)) {
                $this->OutputTable($cells, $type);
                $cells = array();
                $row = 0;
                $text_colgroups = '';
            }

            while (preg_match("/<col\s([^\/]*?)\/>(.*)/", $line, $matches)) {
                $line = $matches[2];
                $text_colgroups .= '|' . $this->GetTableAttribute($matches[1]);
            }
            $this->body[] = $text_colgroups . "|c\n";
        }*/
        elseif (preg_match("/<colgroup>/", $line)) {
            if (count($cells)) {
                $this->OutputTable($cells, $type);
                $cells = array();
                $row = 0;
            }
            $text = '';
            while (preg_match("/<col\s([^\/]*?)\/>(.*)/", $line, $matches)) {
                $line = $matches[2];
                $text .= '|' . $this->GetTableAttribute($matches[1]);
            }
            $this->body[] = $text . "|c\n";
        }
        // ヘッダ・ボディ・フッタの開始
        elseif (preg_match("/<t((h)ead|body|(f)oot)>/", $line, $matches)) {
            $cells = array();
            $type = !empty($matches[2]) ? $matches[2] : (!empty($matches[3]) ? $matches[3] : '');
            $row = 0;
        }
        // ヘッダ・ボディ・フッタの終わり
        elseif (preg_match("/<\/t(head|body|foot)>/", $line)) {
            $this->OutputTable($cells, $type, $delm);
            $cells = array();
        }
    }
    
    // テーブルの属性を取得
    public function GetTableAttribute($attribute)
    {
        $text = '';
        //$pattern = "/rgb\((\d+),\s(\d+),\s(\d+)\)/ie";
        //$attribute = preg_replace($pattern, 'sprintf("#%02x%02x%02x", "$1", "$2", "$3")', $attribute);
        // 文字サイズ
        if (preg_match("/font-size:\s?(\d+)px/i", $attribute, $matches)) {
            $text .= 'SIZE(' . $matches[1] . '):';
        }
        // 背景色
        if (preg_match("/background-color:\s?([#0-9a-z]+)/i", $attribute, $matches)) {
            $text .= 'BGCOLOR(' . $matches[1] . '):';
        }
        // 文字色
        if (preg_match("/(\"|\s)color:\s?([#0-9a-z]+)/i", $attribute, $matches)) {
            $text .= 'COLOR(' . $matches[2] . '):';
        }
        // 整列
        if (preg_match("/align=\"(left|center|right)\"/", $attribute, $matches)) {
            $text .= strtoupper($matches[1]) . ':';
        }
        // 横幅
        if (preg_match("/width=\"(\d+)\"/", $attribute, $matches)) {
            $text .= $matches[1];
        }
        
        return $text;
    }
    
    // テーブルを出力
    public function OutputTable($cells, $type, $delm='|')
    {
        $row = count($cells);
        // $col = count($cells[1]);
        $col = isset($cells[1]) ? count($cells[1]) : 0;
        for ($i = 1; $i <= $row; $i++) {
            for ($j = 1; $j <= $col; $j++) {
                $this->body[] = $delm . $cells[$i][$j];
            }
            if ($delm != ',') {
                $this->body[] = '|' . $type . "\n";
            } else {
                $this->body[] = $type . "\n";
            }
        }
    }
    
    // 段落
    public function Paragraph($line)
    {
        if (preg_match("/<(p|div)(\sstyle=\"text-align:\s*(left|center|right);?\s?\")?>(.*)/", $line, $matches)) {
            if ($matches[1] == 'p') {
                $this->OutputLine();
            }
            $line = ($matches[3] ? (strtoupper($matches[3]) . ':') : '') . $matches[4];
        }
        if (preg_match("/(.*)<\/(p|div)>/", $line, $matches)) {
            // 多分これ逆だと思います。 byはいふん
            if ($matches[1]) {
                $this->OutputLine('', $matches[1]);
            } elseif ($matches[2] == 'p') {
                $this->OutputLine($matches[1]);
            }
        } elseif ($line) {
            $this->OutputLine('', $line);
        }
    }
    
    // インライン要素
    public function Inline($line)
    {
        //$line = $this->EncodeSpecialChars($line);
        $line = preg_replace("/\n/", "", $line);
        // 水平線
        if ($this->GetDiv() != 'Heading' && $this->GetDiv() != 'Table') {
            $line = preg_replace("/<hr(\sclass=\"full_hr\")?\s\/>/", "\n----\n", $line);
            $line = preg_replace("/<hr\sclass=\"short_line\"\s\/>/", "\n#hr\n", $line);
        }
        // プラグイン
        $pattern = "/<span\s[^>]*?class=\"(plugin|ref)\".*>(.*?);<\/span>/";
        $line = preg_replace_callback($pattern, array(&$this, 'InlinePlugin'), $line);
        // リンク
        $line = preg_replace_callback("/<a\shref=\"(.*?)\">(.*?)<\/a>/", array(&$this, 'Link'), $line);
        // アンカー
        $line = preg_replace("/<a\s.*?\s?name=\"(.*?)\"><\/a>/", "&aname($1);", $line);
        $line = preg_replace("/<a\s.*?\s?name=\"(.*?)\">(.*?)<\/a>/", "&aname($1){" . "$2" . "};", $line);
        // 注釈(EasyEdit ver)
        $line = preg_replace("/<span\sclass=\"note\"><img.*?>(.*?)<\/span>/", "(($1))", $line);
        // コメント(EasyEdit ver) なぜここがなかったのか...?
        $line = preg_replace("/<span\sclass=\"comment\".*?><img.*?>(.*?)<\/span>/", "//$1", $line);
        // 顔文字、画像
        $line = preg_replace_callback("/\s?<img\s(.*?)>/", array(&$this, 'Image'), $line);

        // 太字
        $line = preg_replace("/<\/?strong>/", "''", $line);
        // 斜体
        $line = preg_replace("/<\/?em>/", "'''", $line);
        // 下線
        $line = preg_replace("/<\/?u>/", "%%%", $line);
        // 取消線
        $line = preg_replace("/<\/?strike>/", "%%", $line);
        $line = preg_replace("/<\/?del>/", "%%", $line);
        if (exist_plugin("sup")) {
            // 上付き文字
            $line = preg_replace("/<sup>(.*?)<\/sup>/", "&sup{" . "$1" . "};", $line);
        } else {
            $line = preg_replace("/<sup>(.*?)<\/sup>/", "SUP{" . "$1" . "}", $line);
        }
        if (exist_plugin("sub")) {
            // 下付き文字・添え字
            $line = preg_replace("/<sub>(.*?)<\/sub>/", "&sub{"."$1"."};", $line);
        } else {
            $line = preg_replace("/<sub>(.*?)<\/sub>/", "SUB{"."$1"."}", $line);
        }
        // 文字のサイズ・色
        $line = preg_replace_callback("/<(\/)?span(.*?)>/", array(&$this, 'Font'), $line);

        // 改行
        global $line_break;
        if ($this->GetDiv() == "Heading" || $this->GetDiv() == "Table") {
            $line = preg_replace("/<br\s\/>/", "&br;", $line);
        } elseif ($line_break) {
            // $line = preg_replace("/<br\s\/>(<br\s\/>)?/e", '("$1" ? "~" : "") . "\n"', $line);
            $line = preg_replace_callback("/<br\s\/>(<br\s\/>)?/", function ($matches) {
                return ($matches[1] ? "~" : "") . "\n";
            }, $line);
        } else {
            $line = preg_replace("/<br\s\/>/", "~\n", $line);
        }
        
        // 無駄な改行を削除
        $line = preg_replace("/\n\n+/", "\n", $line);
        $line = preg_replace("/(^\n|\n$)/", "", $line);

        // タグの除去
        $line = strip_tags($line);
        // スペース
        $line = preg_replace("/&nbsp;/", " ", $line);
        // 特殊文字
        $line = preg_replace("/&quot;/", '"', $line);
        if ($this->GetDiv() == 'Heading' || $this->GetDiv() == 'Table') {
            $line = preg_replace("/\n/", '', $line);
            $line = preg_replace("/&lt;/", "<", $line);
            $line = preg_replace("/&gt;/", ">", $line);
        } else {
            $line = preg_replace("/\s+$/", '', $line);
            $line = preg_replace("/^\s+/m", '', $line);
        }
        
        //$line = $this->DecodeSpecialChars($line);
        return $line;
    }

    // リンク
    public function Link($matches)
    {
        $url = $matches[1];
        $alias = strip_tags($matches[2]);
        return '[[' . (($url == $alias) ? '' : $alias.'>') . $url.']]';
    }
    
    // 顔文字・画像
    public function Image($matches)
    {
        $attribute = $matches[1];
        $src = false;
        $styles = array();

        // 属性抽出
        if (preg_match("/src=\"(.+?)\"/", $attribute, $matches)) {
            $src = $matches[1];
        }
        //echo $attribute;
        if (preg_match("/style=\"(.+?)\"/", $attribute, $matches)) {
            $style = $matches[1];
            //echo $style;
            // スタイル抽出
            if (preg_match("/width:\s?(.+?)\s?;/", $style, $matches)) {
                $styles['width'] = $matches[1];
            }
            if (preg_match("/height:\s?(.+?)\s?;/", $style, $matches)) {
                $styles['height'] = $matches[1];
            }
        }
        if (preg_match("/alt=\"(.*?)\"/", $attribute, $matches)) {
            $alt = $matches[1];
        }

        if ($alt) {
            /*
            // 注釈
            if ($alt == 'Note' && preg_match("/title=\"(.+?)\"/", $attribute, $matches)) {
                echo $matches[1];
                return '((' . $this->DecodeSpecialChars($matches[1]) . '))';
            }
            // コメント
            else if ($alt == 'Comment' && preg_match("/title=\"(.+?)\"/", $attribute, $matches)) {
                $comment = $matches[1];
                $comment = str_replace("___br___", "\n//", $comment);
                $comment = "//" . $this->DecodeSpecialChars($comment);
                array_push($this->protect_data, $comment);
                return "\n___GUIPD" . count($this->protect_data) . "___\n";
            }
            */
            // 顔文字
            // JO1UPK
            // else if (preg_match("/^\[.+\]$/", $alt, $matches)) {
            if (preg_match("/^\[(.+)\]$/", $alt, $matches)) {
                return '&' . $matches[1] . ';';
            }
        }
        if ($src) {
            $size = false;
            if (!empty($styles)) {
                if (substr($styles['width'], -2) == "px") {
                    $styles['width'] = substr($styles['width'], 0, -2);
                }
                if (substr($styles['height'], -2) == "px") {
                    $styles['height'] = substr($styles['height'], 0, -2);
                }


                if (substr($styles['width'], -1) == "%") {
                    $size = $styles['width'] . "%";
                } elseif (substr($styles['height'], -1) == "%") {
                    $size = $styles['height'] . "%";
                } else {
                    $size = $styles['width'] . 'x' . $styles['height'];
                }
            }

            return '&ref(' . $src . (isset($size) ? "," . $size : "") . ');';
        }
        if ($alt) {
            return ' ' . $alt . ' ';
        }
        return '';
    }
    
    // 文字のサイズ・色
    public function Font($matches)
    {
        static $foot_array = array();
        $attribute = $matches[2];
        
        if (!$matches[1]) {
            if (preg_match("/font-size:\s?((\d+)px|[a-z\-]+)/", $attribute, $matches)) {
                if ($matches[2]) {
                    array_unshift($foot_array, '};');
                    return "&size($matches[2]){";
                }
                switch ($matches[1]) {
                    case 'xx-small':	$size = '1'; break;
                    case 'x-small':		$size = '2'; break;
                    case 'small':		$size = '3'; break;
                    case 'medium':		$size = '4'; break;
                    case 'large':		$size = '5'; break;
                    case 'x-large':		$size = '6'; break;
                    case 'xx-large':	$size = '7'; break;
                }
                if ($this->GetDiv() == 'Heading' || $this->GetDiv() == 'Table') {
                    array_unshift($foot_array, '');
                } else {
                    array_unshift($foot_array, "\n");
                }
                return 'SIZE('.$size.'):';
            }
            
            //$pattern = "/rgb\((\d+),\s(\d+),\s(\d+)\)/e";
            $pattern = "/rgb\((\d+),\s(\d+),\s(\d+)\)/";
            //$attribute = preg_replace($pattern, 'sprintf("#%02x%02x%02x", "$1", "$2", "$3")', $attribute);
            $attribute = preg_replace_callback($pattern, function ($matches) {
                return sprintf("#%02x%02x%02x", $matches[1], $matches[2], $matches[3]);
            }, $attribute);

            if (preg_match("/background-color:\s?([#0-9a-z]+)/i", $attribute, $matches)) {
                $bgcolor = $matches[1];
            }
            if (preg_match("/[^-]color:\s?([#0-9a-z]+)/i", $attribute, $matches)) {
                $color = $matches[1];
            }
            if (!empty($color) || !empty($bgcolor)) {
                array_unshift($foot_array, '};');
                return '&color(' . ($color ? $color : '') . ($bgcolor ? (',' . $bgcolor) : '') . '){';
            }

            return '';
        } else {
            return array_shift($foot_array);
        }
    }

    // インライン型プラグイン
    public function InlinePlugin($matches)
    {
        static $pattern, $replace;

        if (!isset($pattern)) {
            $rule = array(
                "/&amp;/"		=> "&",
                "/&#037;&#037;/"	=> "%%",
                "/&#039;&#039;/"	=> "''",
                "/&#091;&#091;/"	=> "[[",
                "/&#093;&#093;/"	=> "]]",
                "/&#123;/"		=> "{",
                "/&#124;/"		=> "|",
                "/&#125;/"		=> "}"
            );
            $pattern = array_keys($rule);
            $replace = array_values($rule);
        }

        return preg_replace($pattern, $replace, $matches[2] . ';');
    }
    
    // ブロック要素の開始
    public function StartDiv($element)
    {
        array_unshift($this->parent_div, $element);
        array_unshift($this->level_array, $this->div_level);
        $this->div_level = 0;
    }
    
    // ブロック要素の終了
    public function EndDiv()
    {
        array_shift($this->parent_div);
        $this->div_level = array_shift($this->level_array);
        $this->text = '';
    }
    
    // 親のブロック要素を取得
    public function GetDiv()
    {
        return $this->parent_div[0];
    }
    
    // １行出力
    public function OutputLine($head = '', $line = '', $foot = '')
    {
        if ($line != '') {
            $line = $this->Inline($line);
        }
        //echo "<pre>head: " . $head . ", line: " . $line . ", foot: " . $foot . "</pre>";

        $this->body[] = $head . $line . $foot . "\n";
        $this->text = '';
    }

    // 引用などのレベルは３までなので４以上の時は３を返す
    public function GetLevel()
    {
        return ($this->div_level <= 3) ? $this->div_level : 3;
    }

    // エンコード
    public function EncodeSpecialChars($line)
    {
        static $pattern = array("/&amp;/", "/\%\%/", "/\'\'/", "/\[\[/", "/\]\]/", "/\{/", "/\|/", "/\}/");
        static $replace = array("&#038;amp;", "&#037;&#037;", "&#039;&#039;", "&#091;&#091;",
                                "&#093;&#093;", "&#123;", "&#124;", "&#125;");

        return preg_replace($pattern, $replace, $line);
    }

    // 特殊な HTML エンティティを文字に戻す
    public function DecodeSpecialChars($line)
    {
        static $pattern = array("/&amp;/", "/&lt;/", "/&gt;/", "/&quot;/", "/&nbsp;/","/&#091;/","/&#093;/", "/&#038;amp;/");
        static $replace = array('&', '<', '>', '"', ' ','[',']', '&amp;');
        
        return preg_replace($pattern, $replace, $line);
    }
}
