当初の目標 EditToHeaderToFooter

  • 数式機能 ── 数式を簡単に扱える環境の構築 → Latex との連携
  • 字下げ書式 ── ウィキコードの可読性の向上

やっちゃったこと EditToHeaderToFooter

字下げ書式の導入 EditToHeaderToFooter

方針

  • Wikiは行指向のため、コードの見通しが悪くなりがち。字下げを導入でコード可読性を改善。得に複数行プラグインとか。

    仕様 EditToHeaderToFooter

  • 字下げ書式モードでは、行頭の空白は無視。
    • ただし、複数行プラグインの引数に対しては、プラグインの先頭行の字下げ分のみを無視。~例:
          #plugin(){{
              arg
              arg
          }}
      は、
      #plugin(){{
          arg
          arg
      }}
      と等価。
  • 疑似プラグイン #indent で字下げ書式モードに切り替え。
  • 疑似プラグイン #noindent で天突き書式モードに切り替え。
  • デフォルトは天突き書式。
  • 字下げ書式では、行頭が空白の整形済みテキストは無効。

改造 EditToHeaderToFooter

  • pukiwiki.ini.php の適当な場所にて、
      1
    
    + $indent_format = 0;
    
    • $indent_format は天突きと字下げを表すフラグ。
      • ##$indent_format = 0# ⇔ 天突き書式。(デフォルト)
      • ##$indent_format = 1# ⇔ 字下げ書式。
  • convert_html.phpfunction parse 入り口直後にて、
      1
      2
      3
      4
      5
      6
      7
    
         function parse(& $lines)
         {
    +        global $indent_format;
             
             $this->last = & $this;
             $matches = array();
    +        $indent = "";
    
    • $indent = ""; は字下げした深さを格納する変数。複数行プラグインで利用。
  • convert_html.phpfunction parsewhile (!empty($lines))直後にて、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
    
             while (! empty($lines)) {
                 $line   = array_shift($lines);
                 
    +             // Indent extension
    +             if ($indent_format)
    +             {
    +                 preg_match('/^(\s*)(.*)$/', $line, $matches);
    +                 $indent = strlen($matches[1]);
    +                 $line   =        $matches[2] ;
    +             }
    +             
    +             if (rtrim($line) == '#indent') {
    +                 $indent_format = 1;
    +                 continue;
    +             }
    +             if (rtrim($line) == '#noindent') {
    +                 $indent_format = 0;
    +                 continue;
    +             }
                  
                  // Escape comments
                  if (substr($line, 0, 2) == '//') continue;
    
    • $line = array_shift($lines); でラインを切り出すため、この後に記述。
    • Escape commentsなど全てが字下げの影響を受けるため、これらの前に記述。
    • if (rtrim($line) == '#indent') {...} は字下げ書式へ切り替える疑似プラグイン
    • if (rtrim($line) == '#noindent') {...} は天突き書式へ切り替える疑似プラグイン

      課題

  • #indent」と「#noindent」による文脈の切り替えは良くない。
    • 互換性を捨て、字下げ書式に限定すべきか?

H整形の導入

方針

  • 字下げ書式では、行頭の空白は無視されるため、従来の空白整形済みテキストに代わる書式が必要。
  • 先頭が「^」で始まる「H整形/Hat整形」を導入。
    • 正規表現で行頭を示す「^」、また、Wikiで引用を表す「>」や「<」からの連想。
  • 以降では便宜上、空白で始まる整形済みテキストの書式を「S整形/Space整形」で区別。

仕様

  • 行頭が「^」の行は整形済み書式と見なす。基本仕様はS整形に同じ。
  • S整形とH整形の混用は無効。段が一旦切られる。
  • 天突き書式の場合、「^」の左に空白があるとSと見なされ、「^」が残る。

改造

  • convert_html.php で、S整形のクラス class Preコピー。場所は任意だが、流れ的にS整形の直後で、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
    
    !    // '^ 'Hat-beginning sentence
    !    // '^ 'Hat-beginning sentence
    !    // '^ 'Hat-beginning sentence
    !    class HPre extends Element  // for Indent extension
         {
    !        function HPre(& $root, $text)
             {
                 parent::Element();
                 $this->elements[] = htmlspecialchars(substr($text, 1));
             }
     
             function canContain(& $obj)
             {
    !            return is_a($obj, 'HPre');
             }
     
             function & insert(& $obj)
             {
                 $this->elements[] = $obj->elements[0];
                 return $this;
             }
     
             function toString()
             {
                 return $this->wrap(join("\n", $this->elements), 'pre');
             }
         }
    
    • 'HPre' はH整形の識別キーであり、クラス名と一致する必要がある。
  • 同ファイルの class Bodyfunction parsewhile (! empty($lines)) 中にて、Preに関する分岐をコピーし、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
    
             // Pre
    -        if ($head == ' ' || $head == "\t") {
    +        if (!$indent_format && ($head == ' ' || $head == "\t")) {
                 $this->last = & $this->last->add(new Pre($this, $line));
                 continue;
             }
    +        // HPre
    +        if ($head == '^') {
    +            $this->last = & $this->last->add(new HPre($this, $line));
    +            continue;
    +        }
    
    • !$indent_format && は字下げ書式でS整形を無効化する条件。
    • if ($head == '^')はH整形への分岐。

P表の改造

方針

  • 便宜上、行頭が「|」の表を「P表/Pipe表」、行頭が「,」の表を「C表/Comma表」で区別する。
  • P表では、縦方向の関連性が強いので、空白による整列はコードの可読性向上に必要。
  • P表では、行方向のセル連結は右方向(>)でも良いが、左方向の連結も欲しい。
    • コードは左から右、上から下に向かって読むもの、列方向と同様、結合記号より先にセル内容を記述すべき。
  • ~」は列方向のセル連結と、ヘッダ(th)の2つの意味がある上、空白による整列を許すと構文が衝突する。
    • >」からの連想で、左向き連結に「<」を、上向き連結に「^」を導入。
    • 「*」などからの連想で、ヘッダに「^」を導入。

      仕様

  • セル内の先頭と末尾の空白を無視。
  • >」のみのセルは右のセルに連結
  • ^」のみのセルは上のセルに連結
  • <」のみのセルは左のセルに連結(互換仕様)
  • ~」のみのセルは上のセルに連結(互換仕様)
  • *」で始まるセルはヘッダ(th)。
  • ~」で始まる非空白セルはヘッダ(th)。(互換仕様)

改造

  • convert_html.phpclass TableCell にて、
      1
      2
      3
      4
      5
      6
      7
    
     class TableCell extends Element
     {
         var $tag = 'td'; // {td|th}
    +    var $colleft = 0; // ==0: colspan to right by '>'; ==1: colspan to left by '<';
         var $colspan = 1;
         var $rowspan = 1;
         var $style; // is array('width'=>, 'align'=>...);
    
    • $colleftは行結合方向を表す変数。
      • ##$colleft = 0# ⇔ 左に連結。
      • ##$colleft = 1# ⇔ 右に連結。
  • 同クラスのfunction TableCell にて、
      1
      2
      3
      4
      5
    
         function TableCell($text, $is_template = FALSE)
         {
             parent::Element();
             $this->style = $matches = array();
    +        $text = trim($text);
    
    • trim でセル先頭と末尾の空白を除去。
  • 同関数 セル結合の分岐にて、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
    
         if ($text == '>') {
    +        $this->colleft = 0;
             $this->colspan = 0;
    +    } else if ($text == '<') {
    +        $this->colleft = 1;
    +        $this->colspan = 0;
    -    } else if ($text == '~') {
    +    } else if ($text == '^' || $text == '~') {
             $this->rowspan = 0;
    -    } else if (substr($text, 0, 1) == '~') {
    +    } else if (substr($text, 0, 1) == '*' || substr($text, 0, 1) == '~') {
             $this->tag = 'th';
             $text      = substr($text, 1);
         }
    
    • $this->colleft の追加は連結向きの指定
    • if ($text == '<') は左向き連結。
    • $text == '^' || は上向き連結。
    • substr($text, 0, 1) == '*' || はヘッダ。
  • 同関数 セル結合ループにて、右向き連結のオリジナルに対して変更し、さらに左向き連結用にコピー
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
    
    !    // Set colspan and style, from right to left.
    +    $stylerow = NULL;
    +    foreach (array_keys($this->elements) as $nrow) {
    +        $row = & $this->elements[$nrow];
    +        if ($this->types[$nrow] == 'c')
    +            $stylerow = & $row;
    +        $colspan = 1;
    !        foreach (array_reverse(array_keys($row)) as $ncol) {
    !            if ($row[$ncol]->colspan == 0 && $row[$ncol]->colleft == 1) {
    +                ++$colspan;
    +                continue;
    +            }
    +            if ($row[$ncol]->colspan > 0 && $row[$ncol]->$colspan < $colspan) {
    +                $row[$ncol]->colspan = $colspan;
    +            }
    +            if ($stylerow !== NULL) {
    +                $row[$ncol]->setStyle($stylerow[$ncol]->style);
    +                // Inherits column style
    +                while (--$colspan)
    !                    $row[$ncol + $colspan]->setStyle($stylerow[$ncol]->style);
    +            }
    +            $colspan = 1;
    +        }
    +    }
    -    // Set colspan and style
    +    // Set colspan and style, from left to right.
         $stylerow = NULL;
         foreach (array_keys($this->elements) as $nrow) {
             $row = & $this->elements[$nrow];
             if ($this->types[$nrow] == 'c')
                 $stylerow = & $row;
             $colspan = 1;
             foreach (array_keys($row) as $ncol) {
    -            if ($row[$ncol]->colspan == 0) {
    +            if ($row[$ncol]->colspan == 0 && $row[$ncol]->colleft == 0) {
                     ++$colspan;
                     continue;
                 }
    +            if ($row[$ncol]->colspan > 0 && $row[$ncol]->colspan < $colspan) {
                     $row[$ncol]->colspan = $colspan;
    +            }
                 if ($stylerow !== NULL) {
                     $row[$ncol]->setStyle($stylerow[$ncol]->style);
                     // Inherits column style
                     while (--$colspan)
                         $row[$ncol - $colspan]->setStyle($stylerow[$ncol]->style);
                 }
                 $colspan = 1;
             }
         }
    
    • 見やすくするため、コピーの差分を「!」で示した。
    • 内側ループ foreach (array_reverse(array_keys($row)) as $ncol)array_reverse でスキャン向きを反転する仕組み。
    • $row[$ncol + $colspan]->setStyle($stylerow[$ncol]->style); の符号 + は連結方向の影響。

リスト系書式のスキーン変更

方針

  • リストの左マージンや階層間間隔はSkinとして指定すべき。
  • タグ中にstyle属性を直に指定されると、外部CSSが優先順位で負けてしまうので都合が悪い。

改造

  • convert_html.phpclass ListContainer にて、
      1
      2
      3
      4
      5
      6
      7
      8
    
     class ListContainer extends Element
     {
         var $tag;
         var $tag2;
         var $level;
    -    var $style;
    -    var $margin;
    -    var $left_margin;
    
  • 同クラスの function ListContainer にて、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
    
     function ListContainer($tag, $tag2, $head, $text)
     {
         parent::Element();
     
    -    $var_margin      = '_' . $tag . '_margin';
    -    $var_left_margin = '_' . $tag . '_left_margin';
    -    global $$var_margin, $$var_left_margin;
    -
    -    $this->margin      = $$var_margin;
    -    $this->left_margin = $$var_left_margin;
    
  • 同クラスの function setParent にて、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
    
         function setParent(& $parent)
         {
             global $_list_pad_str;
             
             parent::setParent($parent);
             
             $step = $this->level;
             if (isset($parent->parent) && is_a($parent->parent, 'ListContainer'))
                 $step -= $parent->parent->level;
             
             $margin = $this->margin * $step;
             if ($step == $this->level)
                 $margin += $this->left_margin;
    -        
    -        $this->style = sprintf($_list_pad_str, $this->level, $margin, $margin);
         }
    
  • 同クラスの function toString にて、
      1
      2
      3
      4
      5
    
         function toString()
         {
    -        return $this->wrap(parent::toString(), $this->tag, $this->style);
    +        return $this->wrap(parent::toString(), $this->tag, ' class="list'.$this->level.'"');
         }
    
    • 階層毎に異なるスタイルを指定する場合は、従来のまま .list1.list2.list3 を利用。
    • 全階層に共通のスタイルを指定する場合は、タグ名を利用すれば良い。そのスタイルを上書きするスタイルは削除済み。
  • default.ini.php にて、
      1
      2
      3
      4
      5
      6
      7
      8
      9
    
    -/////////////////////////////////////////////////
    -// リスト構造の左マージン
    -$_ul_left_margin = 0;   // リストと画面左端との間隔(px)
    -$_ul_margin = 16;       // リストの階層間の間隔(px)
    -$_ol_left_margin = 0;   // リストと画面左端との間隔(px)
    -$_ol_margin = 16;       // リストの階層間の間隔(px)
    -$_dl_left_margin = 0;   // リストと画面左端との間隔(px)
    -$_dl_margin = 16;        // リストの階層間の間隔(px)
    -$_list_pad_str = ' class="list%d" style="padding-left:%dpx;margin-left:%dpx"';
    

数式プラグイン eq の追加

方針

  • 引数をLaTeXに渡し、dvipngにより画像に変換し、convertで適切に画像処理を施す。
    • なお、convertは画像編集ツール群Imagemagicの一要素。CUIによる画像処理に長けてる。
  • 基本仕様はwikihouse.commath2.inc.phpに従う。
  • WindwosとUnix/Linuxのコマンドの違いを可能な限りプラグイン側で吸収する。
  • 並列処理してもファイル名が衝突がしないようにする。
  • インライン数式の場合、分数線が中心に来るように位置調節する。
    • 例えば、数式の中心揃えは [添付]ではなく、$$ \ffd{\ffd{X}{X}}{X} $$であるべきです。
      $$ \ffd{\ffd{X}{X}}{X} $$

仕様

  • 現時点では、まだUnix/Linuxで確認しておらず。

改造

  • pukiwiki://plugin/eq.inc.php を作成して、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    
    <?php
     
    function plugin_eq_convert()
    {
        $arg  = rtrim(join(func_get_args(), ','), ',');   // 末尾の「,」は波引数が空の場合に現れ、不要物。
        return '<div class="eq">'.plugin_eq_core($arg, 'convert').'</div>';
    }
     
    function plugin_eq_inline()
    {
        $arg  = rtrim(join(func_get_args(), ','), ',');   // 末尾の「,」は波引数が空の場合に現れ、不要物。
        return '<span class="eq">'.plugin_eq_core($arg, 'inline').'</span>';
    }
     
    function plugin_eq_core($arg, $mode)
    {
        $eq_dir = './eq/';                                      // 数式と数式画像のパス
        $err_msg_end_phrase = 'No pages of output.'.PHP_EOL;    // エラー終了を示す文字列(platexの出力依存)
        
        $prefix_convert = 'eq-nc-';             //  ブロック用ファイル衝突を避けるための接頭語
        $prefix_inline  = 'eq-ni-';             // インライン用ファイル衝突を避けるための接頭語
        $tex_frame_convert  = 'convert.tex';    //  ブロック用フレームファイル名
        $tex_frame_inline   = 'inline.tex' ;    // インライン用フレームファイル名
        $eq_replace_phrase  = '\input{eq}' ;    // フレームファイルで数式を埋め込む部を示す文字列
        
        $key = ($mode=='inline') ? $prefix_inline.md5($arg) : $prefix_convert.md5($arg);
        
        $eq  = $key.'.eq';      // 照合用引数出力(保存ファイル)
        $img = $key.'.png';     // 数式の画像出力(保存ファイル)
        
        $old_dir = getcwd();
        chdir($eq_dir);
        
        if (!file_exists($img) || $arg != file_get_contents($eq))
        {
            // 一時ファイル
            $tex  = $key.'.tex';
            $std  = $key.'.std.log';   // Tex の標準出力、エラー出力用
            $dvi  = $key.'.dvi';
            $aux  = $key.'.aux';
            $log  = $key.'.log';
            $box  = $key.'.box.png';
            $face = $key.'.face.png';
            $edge = $key.'.edge.png';
            $bold = $key.'.bold.png';
            
            flush();    // Timeout 対策
            $tex_code  = file_get_contents(($mode=='inline') ? $tex_frame_inline : $tex_frame_convert);
            $tex_code  = str_replace($eq_replace_phrase, $arg, $tex_code);
            
            file_put_contents($eq  ,$arg     );
            file_put_contents($tex ,$tex_code);
            exec('platex -halt-on-error '.$tex.' > '.$std);
            
            flush();    // Timeout 対策
            if (!file_exists($dvi)) // Tex Error
            {
                $err_flag = false;
                $err_code = "Tex Error:\n\n";
                
                if ($log_code = fopen($std, 'r'))
                {
                    while (!feof($log_code)) 
                    {
                        $tex_msg = fgets($log_code);
                        
                             if ($tex_msg[0] == '!'               ) {$err_flag = true ;}
                        else if ($tex_msg    == $err_msg_end_phrase) {$err_flag = false;}
                        
                        if ($err_flag) {$err_code .= $tex_msg;}
                    }
                    fclose($log_code);
                }
                chdir($old_dir);
                return '</p><div><pre class=eq_error_title>'.$arg.'</pre><pre class=eq_error_message>'.$err_code.'</pre></div><p>';
            }
            exec('dvipng -D 720 -T tight --gamma 2.5 -o '.$box.' '.$dvi);
            if ($mode=='inline') {
                        list($width, $height) = getimagesize($box);
                        exec('convert '.$box.' -crop '.($width-16).'x'.($height-16).'+8+8 -bordercolor #FFFFFF               -threshold 50% '.$face);
            } else {
                        exec('convert '.$box.'                                            -bordercolor #FFFFFF -border 16x16 -threshold 50% '.$face);
            }
            exec('convert '.$face.' -edge 1 -negate '.$edge);
            exec('composite -compose multiply '.$face.' '.$edge.' '.$bold);
            exec('convert '.$bold.' -black-threshold 90% -antialias -blur 1 - | convert - -transparent #FFFFFF -antialias '.$img);
            
            flush();    // Timeout 対策
            unlink($tex );
            unlink($std );
            unlink($dvi );
            unlink($aux );
            unlink($log );
            unlink($box );
            unlink($face);
            unlink($edge);
            unlink($bold);
        }
        list($width, $height) = getimagesize($img);
        
        chdir($old_dir);
        return '<img widht='.round($width/7).' height='.round($height/7).' src="'.$eq_dir.$img.'" alt="eq: '.$arg.'" title="'.$arg.'">';
    }
    ?>
  • 複数行eq書式

  • 複数行インラインプラグイン
    • convert_html / function & Factory_span L164 L246 L466 L889 L939 L965 L1041 L1096 L1166
  • エスケープシーケンス
    • make_link L59 L285
  • Code 行番号ズレ
    • codehighlight L83
  • インラインcode
    • code.inc.php L71 L101
  • Code pukiwiki indent 対応
    • codehighlight L169 L208
    • code/line.pukiwiki.php
  • C 拡張
    • code/keyword.c.php
  • diff拡張
    • code/line.diff.php
  • パンミミ
    • topicpath.inc.php L32
  • ページの先頭と末尾へのリンクの移動
    • default.ini.php / 特殊シンボル
    • edit.inc.php L98 L121
  • フッとノートスキーン変更
    • html.php L128

これからやること EditToHeaderToFooter

  • 編集画面のスキーン変更
    • 編集関連ボタンの上下配置
    • テキストエリアの寸法の相対指定
fileline.pukiwiki.php 460件 [詳細] fileline.diff.php 449件 [詳細] fileinline.tex 439件 [詳細] fileconvert_html.php 472件 [詳細] fileeq.inc.php 508件 [詳細] fileconvert.tex 423件 [詳細] filecodehighlight.php 438件 [詳細] filecode.inc.php 475件 [詳細]
    初基 一覧 検索 最新 バックアップ リンク元   ヘルプ   最終更新のRSS