LoginSignup
1
1

More than 5 years have passed since last update.

TCPDFのsvgのdefs/useに関するbugfix

Last updated at Posted at 2017-07-20

 TCPDFでsvgを扱っていてどうにも挙動が不審だったので確認してみたところ、defsの中でuseを使っていると(それを実際にuseで描写すると) useで描写すると 問題が起こっているようだった。
 おそらく下記のパッチで直ると思うんだけど、検証したり英語でpull request書くのが面倒なのでとりあえずここに置いておきます。

(2017-07-21 追記)
 色々検証してみたところdefsの処理が色々おかしかったので、そのあたりを修正しました。
(以前に書いたものは間違いでした。念のため最後に残しておきます)

 他にも文字列出力(text,tspan)周りに問題があることが分かったのですが、大幅な修正が必要そうだったのでそちらについては手をつけていません。
 具体的には

  • 文字列はタグを閉じるときにのみ直前のcontentを出力してるが、tspanの開始時にもそこまでにあるcontentを出力する必要がある
  • tspanタグを閉じた時は状態を前に戻す必要がある

――のような修正が必要と思われます。

(2017-07-24追記)
 文字出力(text,tspan)周りについても修正しました。
 それに合わせて下記のpatchも若干修正しています。
(文字出力patchを適用しなくても問題はありません)

tcpdf-defs.patch
--- tcpdf_min/tcpdf.php Mon Jul 11 01:11:09 2016
+++ tcpdf/tcpdf.php Mon Jul 24 16:52:36 2017
@@ -23700,6 +23700,8 @@
            return;
        }
        if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
+           $attribs['content'] = $this->svgtext;
+           $this->svgtext = '';
            if (isset($attribs['id'])) {
                $attribs['child_elements'] = array();
                $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
@@ -24339,12 +24341,12 @@
            $child_elements = $attribs['child_elements'];
            unset($attribs['child_elements']);
            foreach($child_elements as $child_element) {
+               if (isset($child_element['attribs']['content'])) {
+                   $this->svgtext = $child_element['attribs']['content'];
+               }
                if (empty($child_element['attribs']['closing_tag'])) {
                    $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
                } else {
-                   if (isset($child_element['attribs']['content'])) {
-                       $this->svgtext = $child_element['attribs']['content'];
-                   }
                    $this->endSVGElementHandler('child-tag', $child_element['name']);
                }
            }
@@ -24364,15 +24366,28 @@
        if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
            if (end($this->svgdefs) !== FALSE) {
                $last_svgdefs_id = key($this->svgdefs);
+               $close_param = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
+               $this->svgtext = '';
                if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
-                   foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
-                       if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
-                           $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
-                           return;
+                   $elements_match = 0;
+                   foreach(array_reverse($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) as $child_element) {
+                       if ($child_element['name'] == $name) {
+                           if (isset($child_element['attribs']['id'])) {
+                               if ($elements_match == 0) {
+                                   $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = $close_param;
+                                   return;
+                               }
+                               else {
+                                   $elements_match -= 1;
+                               }
+                           }
+                           else {
+                               $elements_match += 1;
+                           }
                        }
                    }
                    if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
-                       $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
+                       $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = $close_param;
                        return;
                    }
                }

以下、間違っていた記述:

(追記)
 バグの原因は以下だと思われます:

  • use時に閉じタグを処理していない
    • このため閉じタグで処理が必要なgやtext、tspan等でid定義した場合に問題が生じる
  • defsでuseを利用した場合は閉じタグの処理が行なわれる
    • このため単純にuse時の閉じタグ処理を追加すると問題が生じる
--- tcpdf_min/tcpdf.php Mon Jul 11 01:11:09 2016
+++ tcpdf/tcpdf.php Thu Jul 20 14:48:01 2017
@@ -24325,6 +24325,9 @@
                        }
                        $attribs = array_merge($use['attribs'], $attribs);
                        $this->startSVGElementHandler($parser, $use['name'], $attribs);
+                       if ($parser != 'child-tag') {
+                           $this->endSVGElementHandler($parser, $use['name']);
+                       }
                        return;
                    }
                }

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1