Diffs
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample.html
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="Shift_JIS" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml-transitional.dtd">
+<html lang="ja">
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=Shift_JIS" />
+ <link rel="stylesheet" href="sample.css" />
+ <link rel="stylesheet" href="sample2.css" />
+ <style type="text/css">
+div.hoge {
+ color:#00FF00;
+}
+div.hoge a {
+ color:#FFFF00;
+}
+
+div.HUNI {
+ padding:30px;
+}
+ </style>
+</head>
+<body>
+<h1>test</h1>
+
+<br />
+<br/>
+<br>
+<div>hogehoge test</div>
+<div class="hoge">test<a href="" style="font-size:8px">test</a></div>
+<div class="HUNI">‚Ù‚°‚Ù‚°</div>
+<div>‚Ó‚ª‚Ó‚ª‡@</div>
+<a href="mailto:hoge@hogehoge?subject=hoge&body=hogehoe">ƒŠƒ“ƒN</a>
+<![CDATA[cdata]]>
+<![CDATA[multi
+ line
+ cdata]]>
+<![CDATA[cdata with character refference]]>
+<![CDATA[CDATA with > and < ]] ]]>
+</body>
+</html>
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample.php
@@ -0,0 +1,20 @@
+<?php
+$base_dir = dirname(__FILE__) . '/';
+require_once realpath($base_dir .'../HTML/CSS/Mobile.php'); // 開発用
+//require_once 'HTML/CSS/Mobile.php';
+
+if($argc==2) {
+ $file = $argv[1][0]=='/' ? $argv[1] : realpath($base_dir . $argv[1]);
+} else {
+ $file = realpath($base_dir.'sample.html');
+}
+
+$document = file_get_contents(realpath($base_dir.'sample.html'));
+try {
+ echo HTML_CSS_Mobile::getInstance()->setBaseDir($base_dir)->setMode('strict')->addCSSFiles(array('sample3.css', 'sample4.css'))->addCSSFiles('sample5.css')->apply($document);
+} catch (RuntimeException $e) {
+ var_dump($e);
+} catch (Exception $e) {
+ var_dump($e->getMessage());
+}
+
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/nocss.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ </head>
+ <body>
+ Without css.
+ </body>
+</html>
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample.css
@@ -0,0 +1,15 @@
+body {
+ margin: 0px;
+ font-family: MSゴシック;
+}
+h1 {
+ font-size: 11px;
+}
+
+div {
+ font-size: 10px;
+}
+
+a:hover {
+ color: #0000FF;
+}
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample2.css
@@ -0,0 +1,3 @@
+h1 {
+ color: #0000FF;
+}
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample3.css
@@ -0,0 +1,3 @@
+div {
+ color: #FF00FF;
+}
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample4.css
@@ -0,0 +1,3 @@
+div {
+ margin: 0px;
+}
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample/sample5.css
@@ -0,0 +1,3 @@
+h1 {
+ margin: 10px;
+}
属性ã«å¤‰æ›´ãŒã‚ã£ãŸãƒ‘ス: HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/sample
___________________________________________________________________
åå‰: svn:externals
+
HTML_CSS_Mobile/tags/release-1.8.0-20100507003212/HTML/CSS/Mobile.php
@@ -0,0 +1,411 @@
+<?php
+/**
+ * HTML_CSS_Mobile.php
+ *
+ * @author Daichi Kamemoto <daikame@gmail.com>
+ */
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2008 - 2010 Daichi Kamemoto <daikame@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+require_once 'HTML/CSS/Selector2XPath.php';
+require_once 'HTML/CSS.php';
+
+/**
+ * HTML_CSS_Mobile Mobileå‘ã‘ã«å¤–部å‚ç…§/<style>ã‚¿ã‚°ã®ã®CSSをインラインã®styleè¦ç´ ã«åŸ‹ã‚込む
+ * Perlã®HTML::DoCoMoCSS
+ * ( http://search.cpan.org/~tokuhirom/HTML-DoCoMoCSS-0.01/lib/HTML/DoCoMoCSS.pm )
+ * ã®PHP移殖版
+ *
+ * @package
+ * @version 1.8
+ * @copyright 2008 - 2010 yudoufu
+ * @author Daichi Kamemoto(a.k.a yudoufu) <daikame@gmail.com>
+ * @license MIT License
+ */
+class HTML_CSS_Mobile
+{
+ private $base_dir = './';
+ private $mode = 'transit'; // mode-> transit: Exception抑制 strict: 例外発生
+ private $dom;
+ private $dom_xpath;
+ private $css_files = array();
+ private $html_css;
+ private $errors = array();
+
+ /**
+ * getInstance インスタンスをå–å¾—
+ *
+ * @return class
+ */
+ public static function getInstance()
+ {
+ return new HTML_CSS_Mobile();
+ }
+
+ /**
+ * setBaseDir CSSã®ãƒ™ãƒ¼ã‚¹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª(通常ã¯DocumentRoot)ã‚’è¨å®š
+ *
+ * @param string $base_dir
+ * @return class
+ */
+ public function setBaseDir($base_dir)
+ {
+ $this->base_dir = $base_dir;
+ return $this;
+ }
+
+ /**
+ * setMode CSSã®ãƒã‚§ãƒƒã‚¯ãƒ¢ãƒ¼ãƒ‰ã‚’è¨å®š
+ * #TODO: ã‚‚ã£ã¨ã—ã£ã‹ã‚Šãƒ¢ãƒ¼ãƒ‰å®Ÿè£…
+ *
+ * @param string $mode
+ * @return class
+ */
+ public function setMode($mode)
+ {
+ $this->mode = $mode;
+ return $this;
+ }
+
+ /**
+ * addCSSFiles CSSã®ãƒ•ァイルをプãƒã‚°ãƒ©ãƒ å´ã‹ã‚‰èªã¿è¾¼ã‚€
+ *
+ * @param array $files
+ * @return class
+ */
+ public function addCSSFiles($files)
+ {
+ foreach ((array)$files as $file)
+ {
+ if (substr($file, 0, 1) != '/')
+ {
+ $file = $this->base_dir . $file;
+ }
+
+ if (file_exists($file) && is_file($file))
+ {
+ array_push($this->css_files, $file);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * libxmlã®ã‚¨ãƒ©ãƒ¼ã‚’ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã—ãŸçµæžœã‚’è¿”ã™ã€‚
+ * LibXMLErrorオブジェクトã®é…列ãŒè¿”ã£ã¦ãる。
+ *
+ * @return array
+ */
+ public function getXmlErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * apply CSSをインライン化
+ *
+ * @param string $document 変æ›ã‚’行ã†HTML文書
+ * @param string $base_dir CSSã®ãƒ™ãƒ¼ã‚¹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª(setBaseDirより優先)
+ * @return string 変æ›ã•れãŸHTML
+ */
+ public function apply($document, $base_dir = '')
+ {
+ $original_document = $document;
+ /****************************************
+ * å‰å‡¦ç†
+ ****************************************/
+ if ($base_dir)
+ {
+ $this->base_dir = $base_dir;
+ }
+
+ // loadHTML/saveHTMLã®ãƒã‚°ã«å¯¾å¿œã€‚XML宣言ã®ä¸€æ™‚退é¿
+ $declaration = '';
+ if (preg_match('/^<\?xml\s[^>]+?\?>\s*/', $document, $e))
+ {
+ $declaration = $e[0];
+ $document = substr($document, strlen($declaration));
+ }
+
+ // åŒæ§˜ã«ã€<br />ãŒ<br>ã«ãªã£ã¦ã—ã¾ã†å•題ã®ãŸã‚ã«é€€é¿
+ #TODO: meta hr ç‰ã‚‚åŒæ§˜ã ãŒã€å±é™ºãªã®ã§ã•ã‘る。。。本質的ãªè§£æ±ºã«ãªã£ã¦ã„ãªã„。
+ $document = preg_replace('/<(br\s*.*\/)>/', 'HTMLCSSBRESCAPE%$1%::::::::', $document);
+
+ // æ–‡å—å‚照をエスケープ
+ $document = preg_replace('/&(#(?:\d+|x[0-9a-fA-F]+)|[A-Za-z0-9]+);/', 'HTMLCSSINLINERESCAPE%$1%::::::::', $document);
+
+ // CDATAを退é¿
+ $cdata_pattern = '/' . preg_quote('<![CDATA[') . '.*' . preg_quote(']]>') . '/Us';
+ $escaped_cdata = null;
+ if($num_matched = preg_match_all($cdata_pattern, $document, $e))
+ {
+ $escaped_cdata = $e[0];
+ for($i = 0; $i < $num_matched; $i++)
+ {
+ $cdata_replacements[] = "HTMLCSSCDATAPLACEHOLDER$i::::::::";
+ $cdata_patterns[] = $cdata_pattern;
+ }
+ $document = preg_replace($cdata_patterns, $cdata_replacements, $document, 1);
+ }
+
+
+ // 機種ä¾å˜æ–‡å—ãŒã‚¨ãƒ©ãƒ¼ã«ãªã‚‹å•題を回é¿ã™ã‚‹ãŸã‚ã€UTF-8ã«å¤‰æ›ã—ã¦å‡¦ç†
+ $doc_encoding = mb_detect_encoding($document, 'sjis-win, UTF-8, eucjp-win');
+
+ switch (strtolower($doc_encoding))
+ {
+ case 'sjis-win':
+ $html_encoding = 'Shift_JIS';
+ break;
+ case 'eucjp-win':
+ $html_encoding = 'EUC-JP';
+ break;
+ default:
+ $html_encoding = '';
+ break;
+ }
+
+ if ($doc_encoding != 'UTF-8')
+ {
+ $document = str_replace(array('UTF-8', $html_encoding), array('@####UTF8####@', 'UTF-8'), $document);
+ $document = mb_convert_encoding($document, 'UTF-8', $doc_encoding);
+ }
+ /****************************************
+ * 本処ç†
+ ****************************************/
+ // libxmlã®ã‚¨ãƒ©ãƒ¼ã‚’ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°
+ libxml_use_internal_errors(true);
+
+ // XHTMLをパース
+ $this->dom = new DOMDocument();
+ $this->dom->loadHTML($document);
+
+ $this->dom_xpath = new DOMXPath($this->dom);
+
+ $this->loadCSS();
+
+ if (is_null($this->html_css))
+ {
+ return $original_document;
+ }
+
+ // CSSをインライン化
+ $css = $this->html_css->toArray();
+ $add_style = array();
+ foreach ($css as $selector => $style)
+ {
+ // ç–‘ä¼¼è¦ç´ ã¯é€€é¿ã€‚@ルールã¯ã‚¹ãƒ«ãƒ¼(Selector2XPathçš„ã«ãƒã‚°ã§ã‚„ã™ã„)
+ if (strpos($selector, '@') !== false) continue;
+ if (strpos($selector, ':') !== false)
+ {
+ $add_style[] = $selector . '{' . $this->html_css->toInline($selector) . '}';
+ continue;
+ }
+
+ $xpath = HTML_CSS_Selector2XPath::toXPath($selector);
+ $elements = $this->dom_xpath->query($xpath);
+
+ if ($elements->length == 0) continue;
+ // inlineã«ã™ã‚‹CSS文を構æˆ(toInline($selector)ã ã¨h2, h3 ãªã©ã§ã†ã¾ãã„ã‹ãªã„å•題ãŒã‚ã£ãŸãŸã‚)
+ $inline_style = '';
+ foreach ($style as $k => $v)
+ {
+ $inline_style .= $k . ':' . $v . ';';
+ }
+ foreach ($elements as $element)
+ {
+ if ($attr_style = $element->attributes->getNamedItem('style'))
+ {
+ // styleè¦ç´ ãŒå˜åœ¨ã™ã‚‹å ´åˆã¯å‰æ–¹è¿½è¨˜
+ #TODO: ã§ãれã°ã€é‡è¤‡å›žé¿ã‚‚ã—ãŸã„。少ã—ãƒã‚¸ãƒƒã‚¯ãŒã¾ã©ã‚ã£ã“ã—ã„é †åºã«ãªã£ã¦ã—ã¾ã†ã®ã ãŒã€‚。。
+ $attr_style->nodeValue = $inline_style . $attr_style->nodeValue;
+ }
+ else
+ {
+ // styleè¦ç´ ãŒå˜åœ¨ã—ãªã„å ´åˆã¯è¿½åŠ
+ $element->setAttribute('style', $inline_style);
+ }
+ }
+ }
+
+ // 疑似クラスを<style>ã‚¿ã‚°ã¨ã—ã¦è¿½åŠ
+ if (!empty($add_style))
+ {
+ $new_style = implode(PHP_EOL, $add_style);
+ $new_style = str_replace(']]>', ']]]><![CDATA[]>', $new_style);
+ $new_style = implode(PHP_EOL, array('<![CDATA[', $new_style, ']]>'));
+
+ $head = $this->dom_xpath->query('//head');
+ $new_style_node = new DOMElement('style', $new_style);
+ $head->item(0)->appendChild($new_style_node)->setAttribute('type', 'text/css');
+ }
+
+ $result = $this->dom->saveHTML();
+
+
+ // libxmlã®ã‚¨ãƒ©ãƒ¼ã‚’退é¿ã—ã¦å‰Šé™¤
+ $this->errors = libxml_get_errors();
+ libxml_clear_errors();
+
+ /****************************************
+ * 後処ç†
+ ****************************************/
+
+ // æ–‡å—ã‚³ãƒ¼ãƒ‰ã‚’å…ƒã«æˆ»ã™
+ if ($doc_encoding != 'UTF-8')
+ {
+ $result = mb_convert_encoding($result, $doc_encoding, 'UTF-8');
+ $result = str_replace(array('UTF-8', '@####UTF8####@'), array($html_encoding, 'UTF-8'), $result);
+ }
+
+ // エスケープã—ã¦ã„ãŸCDATAを復元
+ if($escaped_cdata) {
+ $result = str_replace($cdata_replacements, $escaped_cdata, $result);
+ }
+
+ // エスケープã—ã¦ã„ãŸå‚照を復元
+ $result = preg_replace('/HTMLCSSINLINERESCAPE%(#(?:\d+|x[0-9a-fA-F]+)|[A-Za-z0-9]+)%::::::::/', '&$1;', $result);
+
+ // <br />を復元
+ $result = preg_replace('/HTMLCSSBRESCAPE%(br\s*.*\/)%::::::::/', '<$1>', $result);
+
+ // 退é¿ã—ãŸXML宣言を復元
+ if (!empty($declaration))
+ {
+ $result = $declaration . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * loadCSS 儿‰€ã§æŒ‡å®šã•れã¦ã„ã‚‹CSSファイルをèªã¿è¾¼ã¿ã€HTML_CSSã®ã‚ªãƒ–ジェクトé…列ã¨ã—ã¦æ ¼ç´ã™ã‚‹
+ *
+ * @return void
+ */
+ private function loadCSS()
+ {
+ // 外部å‚ç…§ã®CSSファイルを抽出ã™ã‚‹
+ $nodes = $this->dom_xpath->query('//link[@rel="stylesheet" or @type="text/css"] | //style[@type="text/css"]');
+
+ foreach ($nodes as $node)
+ {
+ // CSSをパース
+ #TODO: @importã®ã‚µãƒãƒ¼ãƒˆ
+ if ($node->tagName == 'link' && $href = $node->attributes->getNamedItem('href'))
+ {
+ // linkã‚¿ã‚°ã®å ´åˆ
+ if (!file_exists($this->base_dir . $href->nodeValue))
+ {
+ if ($this->mode !== 'strict') continue;
+ throw new UnexpectedValueException('ERROR: ' . $this->base_dir . $href->nodeValue . ' file does not exist');
+ }
+
+ $css_string = file_get_contents($this->base_dir . $href->nodeValue);
+ }
+ else if ($node->tagName == 'style')
+ {
+ // styleã‚¿ã‚°ã®å ´åˆ
+ $css_string = $node->nodeValue;
+ }
+
+ $this->_loadCSS($css_string);
+
+ // èªã¿è¾¼ã¿çµ‚ã‚ã£ãŸãƒŽãƒ¼ãƒ‰ã‚’削除。親ノードãŒå–れãªã„å ´åˆã¯ã‚¹ãƒ«ãƒ¼
+ if ($parent = $node->parentNode)
+ {
+ $parent->removeChild($node);
+ }
+
+ }
+
+ // ãƒ¡ã‚½ãƒƒãƒ‰ã§æŒ‡å®šã—ãŸCSSファイルをèªã¿è¾¼ã‚€
+ if (is_array($this->css_files))
+ {
+ foreach ($this->css_files as $file)
+ {
+ $css_string = '';
+ if (substr($file, 0, 1) != '/')
+ {
+ $file = $this->base_dir . $file;
+ }
+
+ if (file_exists($file) && is_file($file))
+ {
+ $css_string = file_get_contents($file);
+ $this->_loadCSS($css_string);
+ }
+ }
+ }
+ }
+
+ /**
+ * _loadCSS
+ *
+ * @param string $css_string
+ * @return void
+ */
+ private function _loadCSS($css_string)
+ {
+ // css_stringãŒæ–‡å—列以外
+ if (! is_string($css_string)) {
+ // strictモードãªã‚‰ã‚¨ãƒ©ãƒ¼å‡¦ç†ã€ãれ以外ã¯å‡¦ç†ã‚’スã‚ップ
+ if ($this->mode == 'strict') {
+ throw new RuntimeException('ERROR: css content is not string');
+ }
+ return ;
+ }
+
+ // æ–‡å—コードをDOM利用ã®ãŸã‚ã«UTF-8化
+ $css_encoding = mb_detect_encoding($css_string, 'UTF-8, eucjp-win, sjis-win, iso-2022-jp');
+ if ($css_encoding != 'UTF-8')
+ {
+ $css_string = mb_convert_encoding($css_string, 'UTF-8', $css_encoding);
+ }
+
+ if (is_null($this->html_css))
+ {
+ $this->html_css = new HTML_CSS();
+ }
+
+ // CSSをクラスã¸è¿½åŠ
+ $css_error = $this->html_css->parseString($css_string);
+ if ($this->mode == 'strict' && $css_error)
+ {
+ throw new RuntimeException('ERROR: css parse error');
+ }
+ }
+
+ /**
+ * atImportLoad
+ *
+ * @param HTML_CSS instance
+ * @return void
+ */
+ private function atImportLoad($html_css)
+ {
+ #TODO: importã®å–å¾—ãŒä¸Šæ‰‹ã出æ¥ãªã„?
+ }
+}