Index: branches/5.1.x/core/units/pdf/pdf_styles.php =================================================================== diff -u -N -r12127 -r12657 --- branches/5.1.x/core/units/pdf/pdf_styles.php (.../pdf_styles.php) (revision 12127) +++ branches/5.1.x/core/units/pdf/pdf_styles.php (.../pdf_styles.php) (revision 12657) @@ -1,6 +1,6 @@ Prepare(); @@ -43,7 +45,7 @@ $this->SelectorOrder += 1000; } } - + public function ParseStyle($style) { $res = array(); @@ -54,7 +56,7 @@ $res[trim($name)] = trim($value); } } - + /* stylesheet : [ CDO | CDC | S | statement ]*; statement : ruleset | at-rule; @@ -67,10 +69,10 @@ value : [ any | block | ATKEYWORD S* ]+; any : [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES - | DASHMATCH | FUNCTION S* any* ')' + | DASHMATCH | FUNCTION S* any* ')' | '(' S* any* ')' | '[' S* any* ']' ] S*; */ - + function ParseTokens($tokens, $origin=kPDFStylesheet::STYLE_ORIGIN_AUTHOR_NORMAL ) { $this->Buffer[0] = Array(); @@ -98,7 +100,7 @@ foreach ($tokens as $token) {$res .= $token['data'];} return $res; } - + public function ParseDefinitionTokens($tokens) { $mode = 'property'; @@ -133,12 +135,12 @@ if ($mode == 'colon') { trigger_error('Error parsing CSS definition, no colon and/or value after property '.$property, E_USER_WARNING); } - + $properties = $this->ProcessShortHands($properties); - + return $properties; } - + public function ProcessShortHands($properties) { $res = array(); @@ -164,7 +166,7 @@ $res['MARGIN-BOTTOM'] = $regs[3]; $res['MARGIN-LEFT'] = $regs[4]; } - + break; case 'BORDER-TOP': case 'BORDER-RIGHT': @@ -237,7 +239,7 @@ } return $res; } - + public function ParseBorderShorthand($definition) { $res = array(); @@ -255,7 +257,7 @@ } return $res; } - + public function ParseSelectorTokens($tokens, $origin) { $selectors = array(); @@ -274,9 +276,9 @@ } return $this->IdentifySelectors($selectors, $origin); } - + /* - + 'h' => '[0-9a-f]', 'nonascii' => '[\\200-\\377]', 'unicode' => '(\\{h}{1,6}(\r\n|[ \t\r\n\f])?)', @@ -287,7 +289,7 @@ 'string2' => '(\'([^\n\r\f\']|{nl}|{escape})*\')', 'invalid1' => '("([^\n\r\f"]|{nl}|{escape})*?)', 'invalid2' => '(\'([^\n\r\f\']|{nl}|{escape})*?)', - + 'ident' => '-?{nmstart}{nmchar}*', 'name' => '{nmchar}+', 'num' => '([0-9]+|[0-9]*\.[0-9]+)', @@ -297,38 +299,38 @@ 's' => '[ \t\r\n\f]', 'w' => '{s}*', 'nl' => '(\n|\r\n|\r|\f)', - + */ - + /* - + A simple selector is either a type selector or universal selector followed immediately by zero or more attribute selectors, ID selectors, or pseudo-classes, in any order. The simple selector matches if all of its components match. - - A selector is a chain of one or more simple selectors separated by combinators. Combinators are: whitespace, ">", and "+". + + A selector is a chain of one or more simple selectors separated by combinators. Combinators are: whitespace, ">", and "+". Whitespace may appear between a combinator and the simple selectors around it. - + A selector's specificity is calculated as follows: - * count 1 if the selector is a 'style' attribute rather than a selector, 0 otherwise (= a) + * count 1 if the selector is a 'style' attribute rather than a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.) * count the number of ID attributes in the selector (= b) * count the number of other attributes and pseudo-classes in the selector (= c) - * count the number of element names and pseudo-elements in the selector (= d) + * count the number of element names and pseudo-elements in the selector (= d) - The specificity is based only on the form of the selector. - In particular, a selector of the form "[id=p33]" is counted as an attribute selector (a=0, b=0, c=1, d=0), + The specificity is based only on the form of the selector. + In particular, a selector of the form "[id=p33]" is counted as an attribute selector (a=0, b=0, c=1, d=0), even if the id attribute is defined as an "ID" in the source document's DTD. - Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity. - + Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity. + */ - + function IdentifySelectors($selectors, $origin) { $processed = array(); $ident = $this->Macros['ident']; - + foreach ($selectors as $selector) { $parts = preg_split('/[ ]*([ >+])[ ]*/', $selector, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $parsed_selector = array(); @@ -407,7 +409,7 @@ $main = array(); $cur =& $main; - + foreach ($parsed_selector as $parts) { if (isset($parts['combinator'])) { switch ($parts['combinator']) { @@ -447,7 +449,7 @@ } return $processed; } - + public function AppendRule($selector_tokens, $definition_tokens, $origin) { $selectors = $this->ParseSelectorTokens($selector_tokens, $origin); @@ -456,15 +458,15 @@ foreach ($properties as $property => $value) { $definition .= "$property: $value
"; } - + foreach ($selectors as $selector) { $this->Mapping[strtoupper($selector['main'])][] = array('selector' => $selector, 'properties' => $properties); } - + $this->Rules[] = array('selectors' => $selectors, 'properties' => $properties); // echo "appending rule:
selector: ".join(',', $selectors)."
definition:
$definition

"; } - + public function GetTokens($css) { $patterns = array( @@ -474,26 +476,26 @@ '-->' =>'CDC', '~=' =>'INCLUDES', '\\|=' =>'DASHMATCH', - + '{w}\\{' =>'LBRACE', '{w}\\+' =>'PLUS', '{w}\\>' =>'GREATER', '{w},' =>'COMMA', - + '{string}' =>'STRING', '{invalid}' =>'INVALID', /* unclosed string */ - + '{ident}' =>'IDENT', - + '#{name}' =>'HASH', - + '@import' =>'IMPORT_SYM', '@page' =>'PAGE_SYM', '@media' =>'MEDIA_SYM', '@charset' =>'CHARSET_SYM', - + '!{w}important' =>'IMPORTANT_SYM', - + /*'{num}{E}{M}' =>'EMS', '{num}{E}{X}' =>'EXS', '{num}{P}{X}' =>'LENGTH', @@ -510,7 +512,7 @@ '{num}{H}{Z}' =>'FREQ', '{num}{K}{H}{Z}' =>'FREQ', '{num}{ident}' =>'DIMENSION',*/ - + '{num}em' =>'EMS', '{num}ex' =>'EXS', '{num}px' =>'LENGTH', @@ -527,30 +529,30 @@ '{num}hz' =>'FREQ', '{num}khz' =>'FREQ', '{num}{ident}' =>'DIMENSION', - + '{num}%' =>'PERCENTAGE', '{num}' =>'NUMBER', - + 'url\({w}{string}{w}\)' =>'URI', 'url\({w}{url}{w}\)' =>'URI', '{ident}\(' =>'FUNCTION', - + /*'.' =>'*yytext',*/ ); - + $final_patterns = array(); foreach ($patterns as $regexp => $token) { foreach ($this->Macros as $macro => $replacement) { $regexp = str_replace('{'.$macro.'}', $replacement, $regexp); } $final_patterns[$regexp] = $token; } - + $css = preg_replace('/\\/\\*[^*]*\\*+([^\\/*][^*]*\\*+)*\\//', '', $css); $css = preg_replace('/[ \t\r\n\f]+\\/\\*[^*]*\\*+([^\\/*][^*]*\\*+)*\\//', ' ', $css); - + $css = preg_replace('/[ \t\r\n\f]+/', ' ', $css); // remove repeated whitespace - + $matches = array(); $token_indexes = array(); foreach ($final_patterns as $regexp => $token) { @@ -559,7 +561,7 @@ $token_indexes[$token] = 0; } } - + $tokens = array(); $last_token_pos = 0; $i = 0; @@ -575,9 +577,9 @@ } while ($cur_match && $cur_match[1] < $last_token_pos); if ( !$cur_match ) continue; $token_indexes[$token] = $cur_index-1; - if ( $min_pos === false || - ($cur_match[1] < $min_pos - || + if ( $min_pos === false || + ($cur_match[1] < $min_pos + || ( $cur_match[1] == $min_pos && strlen( $cur_match[0] ) > $max_len ) ) ) { @@ -590,7 +592,7 @@ if ($min_pos !== false) { $token_data = $matches[$longest][$token_indexes[$longest]]; if ($token_data[1] > $last_token_pos) { - $text_data = substr($css, $last_token_pos, $token_data[1] - $last_token_pos); + $text_data = substr($css, $last_token_pos, $token_data[1] - $last_token_pos); $tokens[] = array('name' => 'TEXT', 'data' => $text_data); // echo "found token TEXT: [$text_data]
\n"; } @@ -606,10 +608,10 @@ $tokens[] = array('name' => 'TEXT', 'data' => $text_data); // echo "found token FINAL TEXT: [$text_data]
\n"; } - + return $tokens; } - + public function Prepare() { /*$macros = array( @@ -623,7 +625,7 @@ 'string2' => '(\'([^\n\r\f\']|{nl}|{escape})*\')', 'invalid1' => '("([^\n\r\f"]|{nl}|{escape})*?)', 'invalid2' => '(\'([^\n\r\f\']|{nl}|{escape})*?)', - + 'ident' => '-?{nmstart}{nmchar}*', 'name' => '{nmchar}+', 'num' => '([0-9]+|[0-9]*\.[0-9]+)', @@ -633,7 +635,7 @@ 's' => '[ \t\r\n\f]', 'w' => '{s}*', 'nl' => '(\n|\r\n|\r|\f)', - + 'A' => 'a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?', 'C' => 'c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?', 'D' => 'd|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?', @@ -651,7 +653,7 @@ 'X' => 'x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x', 'Z' => 'z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z', );*/ - + $simple = array( 'h' => '[0-9a-f]', 'nonascii' => '[\\200-\\377]', @@ -663,7 +665,7 @@ 'string2' => '(\'([^\n\r\f\']|{nl}|{escape})*\')', 'invalid1' => '("([^\n\r\f"]|{nl}|{escape})*?)', 'invalid2' => '(\'([^\n\r\f\']|{nl}|{escape})*?)', - + 'ident' => '-?{nmstart}{nmchar}*', 'name' => '{nmchar}+', 'num' => '([0-9]+|[0-9]*\.[0-9]+)', @@ -682,21 +684,21 @@ } $replaced_macros[$key] = $replaced; } - + $this->Macros = $replaced_macros; } - + public function GetHTMLVisualPropsSelector($node) { if (!$node->Attributes) return false; $non_visal_props = array( 'ABBR', 'ACCEPT-CHARSET', 'ACCEPT', 'ACCESSKEY', 'ACTION', 'ALT', 'ARCHIVE', 'AXIS', 'CHARSET', 'CHECKED', 'CITE', 'CLASS', 'CLASSID', 'CODE', 'CODEBASE', - 'CODETYPE', 'COLSPAN', 'COORDS', 'DATA', 'DATETIME', 'DECLARE', 'DEFER', 'DIR', 'DISABLED', 'ENCTYPE', 'FOR', 'HEADERS', 'HREF', 'HREFLANG', 'HTTP-EQUIV', - 'ID', 'ISMAP', 'LABEL', 'LANG', 'LANGUAGE', 'LONGDESC', 'MAXLENGTH', 'MEDIA', 'METHOD', 'MULTIPLE', 'NAME', 'NOHREF', 'OBJECT', 'ONBLUR', 'ONCHANGE', + 'CODETYPE', 'COLSPAN', 'COORDS', 'DATA', 'DATETIME', 'DECLARE', 'DEFER', 'DIR', 'DISABLED', 'ENCTYPE', 'FOR', 'HEADERS', 'HREF', 'HREFLANG', 'HTTP-EQUIV', + 'ID', 'ISMAP', 'LABEL', 'LANG', 'LANGUAGE', 'LONGDESC', 'MAXLENGTH', 'MEDIA', 'METHOD', 'MULTIPLE', 'NAME', 'NOHREF', 'OBJECT', 'ONBLUR', 'ONCHANGE', 'ONCLICK', 'ONDBLCLICK', 'ONFOCUS', 'ONKEYDOWN', 'ONKEYPRESS', 'ONKEYUP', 'ONLOAD', 'ONLOAD', 'ONMOUSEDOWN', 'ONMOUSEMOVE', 'ONMOUSEOUT', 'ONMOUSEOVER', - 'ONMOUSEUP', 'ONRESET', 'ONSELECT', 'ONSUBMIT', 'ONUNLOAD', 'ONUNLOAD', 'PROFILE', 'PROMPT', 'READONLY', 'REL', 'REV', 'ROWSPAN', 'SCHEME', 'SCOPE', - 'SELECTED', 'SHAPE', 'SPAN', 'SRC', 'STANDBY', 'START', 'STYLE', 'SUMMARY', 'TITLE', 'USEMAP', + 'ONMOUSEUP', 'ONRESET', 'ONSELECT', 'ONSUBMIT', 'ONUNLOAD', 'ONUNLOAD', 'PROFILE', 'PROMPT', 'READONLY', 'REL', 'REV', 'ROWSPAN', 'SCHEME', 'SCOPE', + 'SELECTED', 'SHAPE', 'SPAN', 'SRC', 'STANDBY', 'START', 'STYLE', 'SUMMARY', 'TITLE', 'USEMAP', 'VALUE', 'VALUETYPE', 'VERSION', ); if ($node->Name != 'LI' && $node->Name != 'OL' && $node->Name != 'UL') { @@ -716,7 +718,7 @@ $processed[0]['order'] = $this->HTMLVisualPropsSelectorOrder++; $processed[0]['specifity'] = 0; $this->Mapping['TD'][] = array( - 'selector' => $processed[0], + 'selector' => $processed[0], 'properties' => $this->ProcessShortHands(array( 'PADDING' => $val.'px', ))); @@ -728,22 +730,22 @@ $mapped_attributes[$key] = $val; } } - + return array( 'selector' => array('main' => $node->Name, 'specifity' => 0, 'order' => $this->HTMLVisualPropsSelectorOrder, 'origin' => kPDFStylesheet::STYLE_ORIGIN_AUTHOR_NORMAL ), 'properties' => $mapped_attributes, ); } return false; } - + public function GetMatchingSelectors($node) { $map = isset($this->Mapping[$node->Name]) ? $this->Mapping[$node->Name] : array(); if (isset($this->Mapping['*'])) { $map = array_merge($map, $this->Mapping['*']); } - + $matching = array(); $i = 0; foreach ($map as $selector) { @@ -752,25 +754,25 @@ $matching[] = $selector; } } - + $html_visual_selector = $this->GetHTMLVisualPropsSelector($node); if ($html_visual_selector) { $matching[] = $html_visual_selector; } - + usort($matching, array($this, 'CmpSelectors')); - + if (isset($node->Attributes['STYLE'])) { $style_selector = array( 'selector' => array('main' => '_STYLE_'), 'properties' => $this->ParseDefinitionTokens ( $this->GetTokens( $node->Attributes['STYLE'] ) ), ); $matching[] = $style_selector; } - + return $matching; } - + public function CmpSelectors($a, $b) { if ($a['selector']['origin'] == $b['selector']['origin']) { @@ -781,13 +783,13 @@ } return $a['selector']['origin'] < $b['selector']['origin'] ? -1 : 1; } - + public function SelectorMatches($selector_data, $node) { if ($selector_data['main'] != '*' && $node->Name != $selector_data['main']) { return false; } - + //check classes if (isset($selector_data['classes'])) { foreach ($selector_data['classes'] as $class) { @@ -797,14 +799,14 @@ } } } - + //check ids if (isset($selector_data['ids'])) { if (!isset($node->Attributes['ID']) || !in_array($node->Attributes['ID'], $selector_data['ids'])) { return false;; } } - + //check atts if (isset($selector_data['atts'])) { if (isset($selector_data['atts']['set'])) { @@ -825,42 +827,42 @@ foreach ($selector_data['atts']['space'] as $att => $value) { if (!isset($node->Attributes[$att]) || !preg_match('/(\A| )+'.preg_quote($value).'( |\Z)+/i', $node->Attributes[$att])) { return false;; - } + } } } if (isset($selector_data['atts']['hypen'])) { foreach ($selector_data['atts']['hypen'] as $att => $value) { if (!isset($node->Attributes[$att]) || !preg_match('/^'.preg_quote($value).'(-|\Z)+/i', $node->Attributes[$att])) { return false;; - } + } } } } - + //check pseudo if (isset($selector_data['pseudo_elements'])) { // we are not a browser - so don't know how to handle this.... return false; } - + if (isset($selector_data['pseudo_classes'])) { // we are not a browser - so don't know how to handle this.... return false; } - + //check comibantors if (isset($selector_data['child_of'])) { if (!$this->SelectorMatches($selector_data['child_of'], $node->Parent)) { return false; } } - + if (isset($selector_data['sibling_of'])) { if (!$this->SelectorMatches($selector_data['sibling_of'], $node->PrevSibling())) { return false; } } - + if (isset($selector_data['descendant_of'])) { $ancestor = $node; do { @@ -869,11 +871,11 @@ } while (!$matches && $ancestor->Parent); if (!$matches) return false; } - + // if we came through here, the selector matches the node return true; } - + public function GetAllProperties($node) { $selectors = $this->GetMatchingSelectors($node);