Нема описа

style.cls.php 71KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435
  1. <?php
  2. /**
  3. * @package dompdf
  4. * @link http://dompdf.github.com/
  5. * @author Benj Carson <benjcarson@digitaljunkies.ca>
  6. * @author Helmut Tischer <htischer@weihenstephan.org>
  7. * @author Fabien Ménager <fabien.menager@gmail.com>
  8. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  9. */
  10. /**
  11. * Represents CSS properties.
  12. *
  13. * The Style class is responsible for handling and storing CSS properties.
  14. * It includes methods to resolve colors and lengths, as well as getters &
  15. * setters for many CSS properites.
  16. *
  17. * Actual CSS parsing is performed in the {@link Stylesheet} class.
  18. *
  19. * @package dompdf
  20. */
  21. class Style {
  22. const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
  23. const CSS_INTEGER = "-?\d+";
  24. /**
  25. * Default font size, in points.
  26. *
  27. * @var float
  28. */
  29. static $default_font_size = 12;
  30. /**
  31. * Default line height, as a fraction of the font size.
  32. *
  33. * @var float
  34. */
  35. static $default_line_height = 1.2;
  36. /**
  37. * Default "absolute" font sizes relative to the default font-size
  38. * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property
  39. * @var array<float>
  40. */
  41. static $font_size_keywords = array(
  42. "xx-small" => 0.6, // 3/5
  43. "x-small" => 0.75, // 3/4
  44. "small" => 0.889, // 8/9
  45. "medium" => 1, // 1
  46. "large" => 1.2, // 6/5
  47. "x-large" => 1.5, // 3/2
  48. "xx-large" => 2.0, // 2/1
  49. );
  50. /**
  51. * List of all inline types. Should really be a constant.
  52. *
  53. * @var array
  54. */
  55. static $INLINE_TYPES = array("inline");
  56. /**
  57. * List of all block types. Should really be a constant.
  58. *
  59. * @var array
  60. */
  61. static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item");
  62. /**
  63. * List of all positionned types. Should really be a constant.
  64. *
  65. * @var array
  66. */
  67. static $POSITIONNED_TYPES = array("relative", "absolute", "fixed");
  68. /**
  69. * List of all table types. Should really be a constant.
  70. *
  71. * @var array;
  72. */
  73. static $TABLE_TYPES = array("table", "inline-table");
  74. /**
  75. * List of valid border styles. Should also really be a constant.
  76. *
  77. * @var array
  78. */
  79. static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid",
  80. "double", "groove", "ridge", "inset", "outset");
  81. /**
  82. * Default style values.
  83. *
  84. * @link http://www.w3.org/TR/CSS21/propidx.html
  85. *
  86. * @var array
  87. */
  88. static protected $_defaults = null;
  89. /**
  90. * List of inherited properties
  91. *
  92. * @link http://www.w3.org/TR/CSS21/propidx.html
  93. *
  94. * @var array
  95. */
  96. static protected $_inherited = null;
  97. /**
  98. * Caches method_exists result
  99. *
  100. * @var array<bool>
  101. */
  102. static protected $_methods_cache = array();
  103. /**
  104. * The stylesheet this style belongs to
  105. *
  106. * @see Stylesheet
  107. * @var Stylesheet
  108. */
  109. protected $_stylesheet; // stylesheet this style is attached to
  110. /**
  111. * Main array of all CSS properties & values
  112. *
  113. * @var array
  114. */
  115. protected $_props;
  116. /* var instead of protected would allow access outside of class */
  117. protected $_important_props;
  118. /**
  119. * Cached property values
  120. *
  121. * @var array
  122. */
  123. protected $_prop_cache;
  124. /**
  125. * Font size of parent element in document tree. Used for relative font
  126. * size resolution.
  127. *
  128. * @var float
  129. */
  130. protected $_parent_font_size; // Font size of parent element
  131. protected $_font_family;
  132. /**
  133. * @var Frame
  134. */
  135. protected $_frame;
  136. /**
  137. * The origin of the style
  138. *
  139. * @var int
  140. */
  141. protected $_origin = Stylesheet::ORIG_AUTHOR;
  142. // private members
  143. /**
  144. * True once the font size is resolved absolutely
  145. *
  146. * @var bool
  147. */
  148. private $__font_size_calculated; // Cache flag
  149. /**
  150. * The computed border radius
  151. */
  152. private $_computed_border_radius = null;
  153. /**
  154. * @var bool
  155. */
  156. public $_has_border_radius = false;
  157. /**
  158. * Class constructor
  159. *
  160. * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
  161. * @param int $origin
  162. */
  163. function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR) {
  164. $this->_props = array();
  165. $this->_important_props = array();
  166. $this->_stylesheet = $stylesheet;
  167. $this->_origin = $origin;
  168. $this->_parent_font_size = null;
  169. $this->__font_size_calculated = false;
  170. if ( !isset(self::$_defaults) ) {
  171. // Shorthand
  172. $d =& self::$_defaults;
  173. // All CSS 2.1 properties, and their default values
  174. $d["azimuth"] = "center";
  175. $d["background_attachment"] = "scroll";
  176. $d["background_color"] = "transparent";
  177. $d["background_image"] = "none";
  178. $d["background_image_resolution"] = "normal";
  179. $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"];
  180. $d["background_position"] = "0% 0%";
  181. $d["background_repeat"] = "repeat";
  182. $d["background"] = "";
  183. $d["border_collapse"] = "separate";
  184. $d["border_color"] = "";
  185. $d["border_spacing"] = "0";
  186. $d["border_style"] = "";
  187. $d["border_top"] = "";
  188. $d["border_right"] = "";
  189. $d["border_bottom"] = "";
  190. $d["border_left"] = "";
  191. $d["border_top_color"] = "";
  192. $d["border_right_color"] = "";
  193. $d["border_bottom_color"] = "";
  194. $d["border_left_color"] = "";
  195. $d["border_top_style"] = "none";
  196. $d["border_right_style"] = "none";
  197. $d["border_bottom_style"] = "none";
  198. $d["border_left_style"] = "none";
  199. $d["border_top_width"] = "medium";
  200. $d["border_right_width"] = "medium";
  201. $d["border_bottom_width"] = "medium";
  202. $d["border_left_width"] = "medium";
  203. $d["border_width"] = "medium";
  204. $d["border_bottom_left_radius"] = "";
  205. $d["border_bottom_right_radius"] = "";
  206. $d["border_top_left_radius"] = "";
  207. $d["border_top_right_radius"] = "";
  208. $d["border_radius"] = "";
  209. $d["border"] = "";
  210. $d["bottom"] = "auto";
  211. $d["caption_side"] = "top";
  212. $d["clear"] = "none";
  213. $d["clip"] = "auto";
  214. $d["color"] = "#000000";
  215. $d["content"] = "normal";
  216. $d["counter_increment"] = "none";
  217. $d["counter_reset"] = "none";
  218. $d["cue_after"] = "none";
  219. $d["cue_before"] = "none";
  220. $d["cue"] = "";
  221. $d["cursor"] = "auto";
  222. $d["direction"] = "ltr";
  223. $d["display"] = "inline";
  224. $d["elevation"] = "level";
  225. $d["empty_cells"] = "show";
  226. $d["float"] = "none";
  227. $d["font_family"] = $stylesheet->get_dompdf()->get_option("default_font");
  228. $d["font_size"] = "medium";
  229. $d["font_style"] = "normal";
  230. $d["font_variant"] = "normal";
  231. $d["font_weight"] = "normal";
  232. $d["font"] = "";
  233. $d["height"] = "auto";
  234. $d["image_resolution"] = "normal";
  235. $d["_dompdf_image_resolution"] = $d["image_resolution"];
  236. $d["_dompdf_keep"] = "";
  237. $d["left"] = "auto";
  238. $d["letter_spacing"] = "normal";
  239. $d["line_height"] = "normal";
  240. $d["list_style_image"] = "none";
  241. $d["list_style_position"] = "outside";
  242. $d["list_style_type"] = "disc";
  243. $d["list_style"] = "";
  244. $d["margin_right"] = "0";
  245. $d["margin_left"] = "0";
  246. $d["margin_top"] = "0";
  247. $d["margin_bottom"] = "0";
  248. $d["margin"] = "";
  249. $d["max_height"] = "none";
  250. $d["max_width"] = "none";
  251. $d["min_height"] = "0";
  252. $d["min_width"] = "0";
  253. $d["opacity"] = "1.0"; // CSS3
  254. $d["orphans"] = "2";
  255. $d["outline_color"] = ""; // "invert" special color is not supported
  256. $d["outline_style"] = "none";
  257. $d["outline_width"] = "medium";
  258. $d["outline"] = "";
  259. $d["overflow"] = "visible";
  260. $d["padding_top"] = "0";
  261. $d["padding_right"] = "0";
  262. $d["padding_bottom"] = "0";
  263. $d["padding_left"] = "0";
  264. $d["padding"] = "";
  265. $d["page_break_after"] = "auto";
  266. $d["page_break_before"] = "auto";
  267. $d["page_break_inside"] = "auto";
  268. $d["pause_after"] = "0";
  269. $d["pause_before"] = "0";
  270. $d["pause"] = "";
  271. $d["pitch_range"] = "50";
  272. $d["pitch"] = "medium";
  273. $d["play_during"] = "auto";
  274. $d["position"] = "static";
  275. $d["quotes"] = "";
  276. $d["richness"] = "50";
  277. $d["right"] = "auto";
  278. $d["size"] = "auto"; // @page
  279. $d["speak_header"] = "once";
  280. $d["speak_numeral"] = "continuous";
  281. $d["speak_punctuation"] = "none";
  282. $d["speak"] = "normal";
  283. $d["speech_rate"] = "medium";
  284. $d["stress"] = "50";
  285. $d["table_layout"] = "auto";
  286. $d["text_align"] = "left";
  287. $d["text_decoration"] = "none";
  288. $d["text_indent"] = "0";
  289. $d["text_transform"] = "none";
  290. $d["top"] = "auto";
  291. $d["transform"] = "none"; // CSS3
  292. $d["transform_origin"] = "50% 50%"; // CSS3
  293. $d["_webkit_transform"] = $d["transform"]; // CSS3
  294. $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3
  295. $d["unicode_bidi"] = "normal";
  296. $d["vertical_align"] = "baseline";
  297. $d["visibility"] = "visible";
  298. $d["voice_family"] = "";
  299. $d["volume"] = "medium";
  300. $d["white_space"] = "normal";
  301. $d["word_wrap"] = "normal";
  302. $d["widows"] = "2";
  303. $d["width"] = "auto";
  304. $d["word_spacing"] = "normal";
  305. $d["z_index"] = "auto";
  306. // for @font-face
  307. $d["src"] = "";
  308. $d["unicode_range"] = "";
  309. // Properties that inherit by default
  310. self::$_inherited = array(
  311. "azimuth",
  312. "background_image_resolution",
  313. "border_collapse",
  314. "border_spacing",
  315. "caption_side",
  316. "color",
  317. "cursor",
  318. "direction",
  319. "elevation",
  320. "empty_cells",
  321. "font_family",
  322. "font_size",
  323. "font_style",
  324. "font_variant",
  325. "font_weight",
  326. "font",
  327. "image_resolution",
  328. "letter_spacing",
  329. "line_height",
  330. "list_style_image",
  331. "list_style_position",
  332. "list_style_type",
  333. "list_style",
  334. "orphans",
  335. "page_break_inside",
  336. "pitch_range",
  337. "pitch",
  338. "quotes",
  339. "richness",
  340. "speak_header",
  341. "speak_numeral",
  342. "speak_punctuation",
  343. "speak",
  344. "speech_rate",
  345. "stress",
  346. "text_align",
  347. "text_indent",
  348. "text_transform",
  349. "visibility",
  350. "voice_family",
  351. "volume",
  352. "white_space",
  353. "word_wrap",
  354. "widows",
  355. "word_spacing",
  356. );
  357. }
  358. }
  359. /**
  360. * "Destructor": forcibly free all references held by this object
  361. */
  362. function dispose() {
  363. clear_object($this);
  364. }
  365. function set_frame(Frame $frame) {
  366. $this->_frame = $frame;
  367. }
  368. function get_frame() {
  369. return $this->_frame;
  370. }
  371. function set_origin($origin) {
  372. $this->_origin = $origin;
  373. }
  374. function get_origin() {
  375. return $this->_origin;
  376. }
  377. /**
  378. * returns the {@link Stylesheet} this Style is associated with.
  379. *
  380. * @return Stylesheet
  381. */
  382. function get_stylesheet() { return $this->_stylesheet; }
  383. /**
  384. * Converts any CSS length value into an absolute length in points.
  385. *
  386. * length_in_pt() takes a single length (e.g. '1em') or an array of
  387. * lengths and returns an absolute length. If an array is passed, then
  388. * the return value is the sum of all elements.
  389. *
  390. * If a reference size is not provided, the default font size is used
  391. * ({@link Style::$default_font_size}).
  392. *
  393. * @param float|array $length the length or array of lengths to resolve
  394. * @param float $ref_size an absolute reference size to resolve percentage lengths
  395. * @return float
  396. */
  397. function length_in_pt($length, $ref_size = null) {
  398. static $cache = array();
  399. if ( !is_array($length) ) {
  400. $length = array($length);
  401. }
  402. if ( !isset($ref_size) ) {
  403. $ref_size = self::$default_font_size;
  404. }
  405. $key = implode("@", $length)."/$ref_size";
  406. if ( isset($cache[$key]) ) {
  407. return $cache[$key];
  408. }
  409. $ret = 0;
  410. foreach ($length as $l) {
  411. if ( $l === "auto" ) {
  412. return "auto";
  413. }
  414. if ( $l === "none" ) {
  415. return "none";
  416. }
  417. // Assume numeric values are already in points
  418. if ( is_numeric($l) ) {
  419. $ret += $l;
  420. continue;
  421. }
  422. if ( $l === "normal" ) {
  423. $ret += $ref_size;
  424. continue;
  425. }
  426. // Border lengths
  427. if ( $l === "thin" ) {
  428. $ret += 0.5;
  429. continue;
  430. }
  431. if ( $l === "medium" ) {
  432. $ret += 1.5;
  433. continue;
  434. }
  435. if ( $l === "thick" ) {
  436. $ret += 2.5;
  437. continue;
  438. }
  439. if ( ($i = mb_strpos($l, "px")) !== false ) {
  440. $dpi = $this->_stylesheet->get_dompdf()->get_option("dpi");
  441. $ret += ( mb_substr($l, 0, $i) * 72 ) / $dpi;
  442. continue;
  443. }
  444. if ( ($i = mb_strpos($l, "pt")) !== false ) {
  445. $ret += (float)mb_substr($l, 0, $i);
  446. continue;
  447. }
  448. if ( ($i = mb_strpos($l, "%")) !== false ) {
  449. $ret += (float)mb_substr($l, 0, $i)/100 * $ref_size;
  450. continue;
  451. }
  452. if ( ($i = mb_strpos($l, "rem")) !== false ) {
  453. $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->get_tree()->get_root()->get_style()->font_size;
  454. continue;
  455. }
  456. if ( ($i = mb_strpos($l, "em")) !== false ) {
  457. $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size");
  458. continue;
  459. }
  460. if ( ($i = mb_strpos($l, "cm")) !== false ) {
  461. $ret += mb_substr($l, 0, $i) * 72 / 2.54;
  462. continue;
  463. }
  464. if ( ($i = mb_strpos($l, "mm")) !== false ) {
  465. $ret += mb_substr($l, 0, $i) * 72 / 25.4;
  466. continue;
  467. }
  468. // FIXME: em:ex ratio?
  469. if ( ($i = mb_strpos($l, "ex")) !== false ) {
  470. $ret += mb_substr($l, 0, $i) * $this->__get("font_size") / 2;
  471. continue;
  472. }
  473. if ( ($i = mb_strpos($l, "in")) !== false ) {
  474. $ret += (float)mb_substr($l, 0, $i) * 72;
  475. continue;
  476. }
  477. if ( ($i = mb_strpos($l, "pc")) !== false ) {
  478. $ret += (float)mb_substr($l, 0, $i) * 12;
  479. continue;
  480. }
  481. // Bogus value
  482. $ret += $ref_size;
  483. }
  484. return $cache[$key] = $ret;
  485. }
  486. /**
  487. * Set inherited properties in this style using values in $parent
  488. *
  489. * @param Style $parent
  490. *
  491. * @return Style
  492. */
  493. function inherit(Style $parent) {
  494. // Set parent font size
  495. $this->_parent_font_size = $parent->get_font_size();
  496. foreach (self::$_inherited as $prop) {
  497. //inherit the !important property also.
  498. //if local property is also !important, don't inherit.
  499. if ( isset($parent->_props[$prop]) &&
  500. ( !isset($this->_props[$prop]) ||
  501. ( isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop]) )
  502. )
  503. ) {
  504. if ( isset($parent->_important_props[$prop]) ) {
  505. $this->_important_props[$prop] = true;
  506. }
  507. //see __set and __get, on all assignments clear cache!
  508. $this->_prop_cache[$prop] = null;
  509. $this->_props[$prop] = $parent->_props[$prop];
  510. }
  511. }
  512. foreach ($this->_props as $prop => $value) {
  513. if ( $value === "inherit" ) {
  514. if ( isset($parent->_important_props[$prop]) ) {
  515. $this->_important_props[$prop] = true;
  516. }
  517. //do not assign direct, but
  518. //implicite assignment through __set, redirect to specialized, get value with __get
  519. //This is for computing defaults if the parent setting is also missing.
  520. //Therefore do not directly assign the value without __set
  521. //set _important_props before that to be able to propagate.
  522. //see __set and __get, on all assignments clear cache!
  523. //$this->_prop_cache[$prop] = null;
  524. //$this->_props[$prop] = $parent->_props[$prop];
  525. //props_set for more obvious explicite assignment not implemented, because
  526. //too many implicite uses.
  527. // $this->props_set($prop, $parent->$prop);
  528. $this->__set($prop, $parent->__get($prop));
  529. }
  530. }
  531. return $this;
  532. }
  533. /**
  534. * Override properties in this style with those in $style
  535. *
  536. * @param Style $style
  537. */
  538. function merge(Style $style) {
  539. //treat the !important attribute
  540. //if old rule has !important attribute, override with new rule only if
  541. //the new rule is also !important
  542. foreach($style->_props as $prop => $val ) {
  543. if (isset($style->_important_props[$prop])) {
  544. $this->_important_props[$prop] = true;
  545. //see __set and __get, on all assignments clear cache!
  546. $this->_prop_cache[$prop] = null;
  547. $this->_props[$prop] = $val;
  548. }
  549. else if ( !isset($this->_important_props[$prop]) ) {
  550. //see __set and __get, on all assignments clear cache!
  551. $this->_prop_cache[$prop] = null;
  552. $this->_props[$prop] = $val;
  553. }
  554. }
  555. if ( isset($style->_props["font_size"]) ) {
  556. $this->__font_size_calculated = false;
  557. }
  558. }
  559. /**
  560. * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb")
  561. * based on the provided CSS color value.
  562. *
  563. * @param string $color
  564. * @return array
  565. */
  566. function munge_color($color) {
  567. return CSS_Color::parse($color);
  568. }
  569. /* direct access to _important_props array from outside would work only when declared as
  570. * 'var $_important_props;' instead of 'protected $_important_props;'
  571. * Don't call _set/__get on missing attribute. Therefore need a special access.
  572. * Assume that __set will be also called when this is called, so do not check validity again.
  573. * Only created, if !important exists -> always set true.
  574. */
  575. function important_set($prop) {
  576. $prop = str_replace("-", "_", $prop);
  577. $this->_important_props[$prop] = true;
  578. }
  579. function important_get($prop) {
  580. return isset($this->_important_props[$prop]);
  581. }
  582. /**
  583. * PHP5 overloaded setter
  584. *
  585. * This function along with {@link Style::__get()} permit a user of the
  586. * Style class to access any (CSS) property using the following syntax:
  587. * <code>
  588. * Style->margin_top = "1em";
  589. * echo (Style->margin_top);
  590. * </code>
  591. *
  592. * __set() automatically calls the provided set function, if one exists,
  593. * otherwise it sets the property directly. Typically, __set() is not
  594. * called directly from outside of this class.
  595. *
  596. * On each modification clear cache to return accurate setting.
  597. * Also affects direct settings not using __set
  598. * For easier finding all assignments, attempted to allowing only explicite assignment:
  599. * Very many uses, e.g. frame_reflower.cls.php -> for now leave as it is
  600. * function __set($prop, $val) {
  601. * throw new DOMPDF_Exception("Implicite replacement of assignment by __set. Not good.");
  602. * }
  603. * function props_set($prop, $val) { ... }
  604. *
  605. * @param string $prop the property to set
  606. * @param mixed $val the value of the property
  607. *
  608. */
  609. function __set($prop, $val) {
  610. $prop = str_replace("-", "_", $prop);
  611. $this->_prop_cache[$prop] = null;
  612. if ( !isset(self::$_defaults[$prop]) ) {
  613. global $_dompdf_warnings;
  614. $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property.";
  615. return;
  616. }
  617. if ( $prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false ) {
  618. $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val)));
  619. $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
  620. }
  621. $method = "set_$prop";
  622. if ( !isset(self::$_methods_cache[$method]) ) {
  623. self::$_methods_cache[$method] = method_exists($this, $method);
  624. }
  625. if ( self::$_methods_cache[$method] ) {
  626. $this->$method($val);
  627. }
  628. else {
  629. $this->_props[$prop] = $val;
  630. }
  631. }
  632. /**
  633. * PHP5 overloaded getter
  634. * Along with {@link Style::__set()} __get() provides access to all CSS
  635. * properties directly. Typically __get() is not called directly outside
  636. * of this class.
  637. * On each modification clear cache to return accurate setting.
  638. * Also affects direct settings not using __set
  639. *
  640. * @param string $prop
  641. *
  642. * @throws DOMPDF_Exception
  643. * @return mixed
  644. */
  645. function __get($prop) {
  646. if ( !isset(self::$_defaults[$prop]) ) {
  647. throw new DOMPDF_Exception("'$prop' is not a valid CSS2 property.");
  648. }
  649. if ( isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null ) {
  650. return $this->_prop_cache[$prop];
  651. }
  652. $method = "get_$prop";
  653. // Fall back on defaults if property is not set
  654. if ( !isset($this->_props[$prop]) ) {
  655. $this->_props[$prop] = self::$_defaults[$prop];
  656. }
  657. if ( !isset(self::$_methods_cache[$method]) ) {
  658. self::$_methods_cache[$method] = method_exists($this, $method);
  659. }
  660. if ( self::$_methods_cache[$method] ) {
  661. return $this->_prop_cache[$prop] = $this->$method();
  662. }
  663. return $this->_prop_cache[$prop] = $this->_props[$prop];
  664. }
  665. function get_font_family_raw(){
  666. return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
  667. }
  668. /**
  669. * Getter for the 'font-family' CSS property.
  670. * Uses the {@link Font_Metrics} class to resolve the font family into an
  671. * actual font file.
  672. *
  673. * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
  674. * @throws DOMPDF_Exception
  675. *
  676. * @return string
  677. */
  678. function get_font_family() {
  679. if ( isset($this->_font_family) ) {
  680. return $this->_font_family;
  681. }
  682. $DEBUGCSS=DEBUGCSS; //=DEBUGCSS; Allow override of global setting for ad hoc debug
  683. // Select the appropriate font. First determine the subtype, then check
  684. // the specified font-families for a candidate.
  685. // Resolve font-weight
  686. $weight = $this->__get("font_weight");
  687. if ( is_numeric($weight) ) {
  688. if ( $weight < 600 ) {
  689. $weight = "normal";
  690. }
  691. else {
  692. $weight = "bold";
  693. }
  694. }
  695. else if ( $weight === "bold" || $weight === "bolder" ) {
  696. $weight = "bold";
  697. }
  698. else {
  699. $weight = "normal";
  700. }
  701. // Resolve font-style
  702. $font_style = $this->__get("font_style");
  703. if ( $weight === "bold" && ($font_style === "italic" || $font_style === "oblique") ) {
  704. $subtype = "bold_italic";
  705. }
  706. else if ( $weight === "bold" && $font_style !== "italic" && $font_style !== "oblique" ) {
  707. $subtype = "bold";
  708. }
  709. else if ( $weight !== "bold" && ($font_style === "italic" || $font_style === "oblique") ) {
  710. $subtype = "italic";
  711. }
  712. else {
  713. $subtype = "normal";
  714. }
  715. // Resolve the font family
  716. if ( $DEBUGCSS ) {
  717. print "<pre>[get_font_family:";
  718. print '('.$this->_props["font_family"].'.'.$font_style.'.'.$this->__get("font_weight").'.'.$weight.'.'.$subtype.')';
  719. }
  720. $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
  721. $font = null;
  722. foreach($families as $family) {
  723. //remove leading and trailing string delimiters, e.g. on font names with spaces;
  724. //remove leading and trailing whitespace
  725. $family = trim($family, " \t\n\r\x0B\"'");
  726. if ( $DEBUGCSS ) {
  727. print '('.$family.')';
  728. }
  729. $font = Font_Metrics::get_font($family, $subtype);
  730. if ( $font ) {
  731. if ($DEBUGCSS) print '('.$font.")get_font_family]\n</pre>";
  732. return $this->_font_family = $font;
  733. }
  734. }
  735. $family = null;
  736. if ( $DEBUGCSS ) {
  737. print '(default)';
  738. }
  739. $font = Font_Metrics::get_font($family, $subtype);
  740. if ( $font ) {
  741. if ( $DEBUGCSS ) print '('.$font.")get_font_family]\n</pre>";
  742. return$this->_font_family = $font;
  743. }
  744. throw new DOMPDF_Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] ."'");
  745. }
  746. /**
  747. * Returns the resolved font size, in points
  748. *
  749. * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
  750. * @return float
  751. */
  752. function get_font_size() {
  753. if ( $this->__font_size_calculated ) {
  754. return $this->_props["font_size"];
  755. }
  756. if ( !isset($this->_props["font_size"]) ) {
  757. $fs = self::$_defaults["font_size"];
  758. }
  759. else {
  760. $fs = $this->_props["font_size"];
  761. }
  762. if ( !isset($this->_parent_font_size) ) {
  763. $this->_parent_font_size = self::$default_font_size;
  764. }
  765. switch ((string)$fs) {
  766. case "xx-small":
  767. case "x-small":
  768. case "small":
  769. case "medium":
  770. case "large":
  771. case "x-large":
  772. case "xx-large":
  773. $fs = self::$default_font_size * self::$font_size_keywords[$fs];
  774. break;
  775. case "smaller":
  776. $fs = 8/9 * $this->_parent_font_size;
  777. break;
  778. case "larger":
  779. $fs = 6/5 * $this->_parent_font_size;
  780. break;
  781. default:
  782. break;
  783. }
  784. // Ensure relative sizes resolve to something
  785. if ( ($i = mb_strpos($fs, "em")) !== false ) {
  786. $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
  787. }
  788. else if ( ($i = mb_strpos($fs, "ex")) !== false ) {
  789. $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
  790. }
  791. else {
  792. $fs = $this->length_in_pt($fs);
  793. }
  794. //see __set and __get, on all assignments clear cache!
  795. $this->_prop_cache["font_size"] = null;
  796. $this->_props["font_size"] = $fs;
  797. $this->__font_size_calculated = true;
  798. return $this->_props["font_size"];
  799. }
  800. /**
  801. * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
  802. * @return float
  803. */
  804. function get_word_spacing() {
  805. if ( $this->_props["word_spacing"] === "normal" ) {
  806. return 0;
  807. }
  808. return $this->_props["word_spacing"];
  809. }
  810. /**
  811. * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
  812. * @return float
  813. */
  814. function get_letter_spacing() {
  815. if ( $this->_props["letter_spacing"] === "normal" ) {
  816. return 0;
  817. }
  818. return $this->_props["letter_spacing"];
  819. }
  820. /**
  821. * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
  822. * @return float
  823. */
  824. function get_line_height() {
  825. $line_height = $this->_props["line_height"];
  826. if ( $line_height === "normal" ) {
  827. return self::$default_line_height * $this->get_font_size();
  828. }
  829. if ( is_numeric($line_height) ) {
  830. return $this->length_in_pt( $line_height . "em", $this->get_font_size());
  831. }
  832. return $this->length_in_pt( $line_height, $this->_parent_font_size );
  833. }
  834. /**
  835. * Returns the color as an array
  836. *
  837. * The array has the following format:
  838. * <code>array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")</code>
  839. *
  840. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
  841. * @return array
  842. */
  843. function get_color() {
  844. return $this->munge_color( $this->_props["color"] );
  845. }
  846. /**
  847. * Returns the background color as an array
  848. *
  849. * The returned array has the same format as {@link Style::get_color()}
  850. *
  851. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
  852. * @return array
  853. */
  854. function get_background_color() {
  855. return $this->munge_color( $this->_props["background_color"] );
  856. }
  857. /**
  858. * Returns the background position as an array
  859. *
  860. * The returned array has the following format:
  861. * <code>array(x,y, "x" => x, "y" => y)</code>
  862. *
  863. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
  864. * @return array
  865. */
  866. function get_background_position() {
  867. $tmp = explode(" ", $this->_props["background_position"]);
  868. switch ($tmp[0]) {
  869. case "left":
  870. $x = "0%";
  871. break;
  872. case "right":
  873. $x = "100%";
  874. break;
  875. case "top":
  876. $y = "0%";
  877. break;
  878. case "bottom":
  879. $y = "100%";
  880. break;
  881. case "center":
  882. $x = "50%";
  883. $y = "50%";
  884. break;
  885. default:
  886. $x = $tmp[0];
  887. break;
  888. }
  889. if ( isset($tmp[1]) ) {
  890. switch ($tmp[1]) {
  891. case "left":
  892. $x = "0%";
  893. break;
  894. case "right":
  895. $x = "100%";
  896. break;
  897. case "top":
  898. $y = "0%";
  899. break;
  900. case "bottom":
  901. $y = "100%";
  902. break;
  903. case "center":
  904. if ( $tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center" ) {
  905. $y = "50%";
  906. }
  907. else {
  908. $x = "50%";
  909. }
  910. break;
  911. default:
  912. $y = $tmp[1];
  913. break;
  914. }
  915. }
  916. else {
  917. $y = "50%";
  918. }
  919. if ( !isset($x) ) {
  920. $x = "0%";
  921. }
  922. if ( !isset($y) ) {
  923. $y = "0%";
  924. }
  925. return array(
  926. 0 => $x, "x" => $x,
  927. 1 => $y, "y" => $y,
  928. );
  929. }
  930. /**
  931. * Returns the background as it is currently stored
  932. *
  933. * (currently anyway only for completeness.
  934. * not used for further processing)
  935. *
  936. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
  937. * @return string
  938. */
  939. function get_background_attachment() {
  940. return $this->_props["background_attachment"];
  941. }
  942. /**
  943. * Returns the background_repeat as it is currently stored
  944. *
  945. * (currently anyway only for completeness.
  946. * not used for further processing)
  947. *
  948. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
  949. * @return string
  950. */
  951. function get_background_repeat() {
  952. return $this->_props["background_repeat"];
  953. }
  954. /**
  955. * Returns the background as it is currently stored
  956. *
  957. * (currently anyway only for completeness.
  958. * not used for further processing, but the individual get_background_xxx)
  959. *
  960. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
  961. * @return string
  962. */
  963. function get_background() {
  964. return $this->_props["background"];
  965. }
  966. /**#@+
  967. * Returns the border color as an array
  968. *
  969. * See {@link Style::get_color()}
  970. *
  971. * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
  972. * @return array
  973. */
  974. function get_border_top_color() {
  975. if ( $this->_props["border_top_color"] === "" ) {
  976. //see __set and __get, on all assignments clear cache!
  977. $this->_prop_cache["border_top_color"] = null;
  978. $this->_props["border_top_color"] = $this->__get("color");
  979. }
  980. return $this->munge_color($this->_props["border_top_color"]);
  981. }
  982. function get_border_right_color() {
  983. if ( $this->_props["border_right_color"] === "" ) {
  984. //see __set and __get, on all assignments clear cache!
  985. $this->_prop_cache["border_right_color"] = null;
  986. $this->_props["border_right_color"] = $this->__get("color");
  987. }
  988. return $this->munge_color($this->_props["border_right_color"]);
  989. }
  990. function get_border_bottom_color() {
  991. if ( $this->_props["border_bottom_color"] === "" ) {
  992. //see __set and __get, on all assignments clear cache!
  993. $this->_prop_cache["border_bottom_color"] = null;
  994. $this->_props["border_bottom_color"] = $this->__get("color");
  995. }
  996. return $this->munge_color($this->_props["border_bottom_color"]);
  997. }
  998. function get_border_left_color() {
  999. if ( $this->_props["border_left_color"] === "" ) {
  1000. //see __set and __get, on all assignments clear cache!
  1001. $this->_prop_cache["border_left_color"] = null;
  1002. $this->_props["border_left_color"] = $this->__get("color");
  1003. }
  1004. return $this->munge_color($this->_props["border_left_color"]);
  1005. }
  1006. /**#@-*/
  1007. /**#@+
  1008. * Returns the border width, as it is currently stored
  1009. *
  1010. * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties
  1011. * @return float|string
  1012. */
  1013. function get_border_top_width() {
  1014. $style = $this->__get("border_top_style");
  1015. return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0;
  1016. }
  1017. function get_border_right_width() {
  1018. $style = $this->__get("border_right_style");
  1019. return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0;
  1020. }
  1021. function get_border_bottom_width() {
  1022. $style = $this->__get("border_bottom_style");
  1023. return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0;
  1024. }
  1025. function get_border_left_width() {
  1026. $style = $this->__get("border_left_style");
  1027. return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0;
  1028. }
  1029. /**#@-*/
  1030. /**
  1031. * Return an array of all border properties.
  1032. *
  1033. * The returned array has the following structure:
  1034. * <code>
  1035. * array("top" => array("width" => [border-width],
  1036. * "style" => [border-style],
  1037. * "color" => [border-color (array)]),
  1038. * "bottom" ... )
  1039. * </code>
  1040. *
  1041. * @return array
  1042. */
  1043. function get_border_properties() {
  1044. return array(
  1045. "top" => array(
  1046. "width" => $this->__get("border_top_width"),
  1047. "style" => $this->__get("border_top_style"),
  1048. "color" => $this->__get("border_top_color"),
  1049. ),
  1050. "bottom" => array(
  1051. "width" => $this->__get("border_bottom_width"),
  1052. "style" => $this->__get("border_bottom_style"),
  1053. "color" => $this->__get("border_bottom_color"),
  1054. ),
  1055. "right" => array(
  1056. "width" => $this->__get("border_right_width"),
  1057. "style" => $this->__get("border_right_style"),
  1058. "color" => $this->__get("border_right_color"),
  1059. ),
  1060. "left" => array(
  1061. "width" => $this->__get("border_left_width"),
  1062. "style" => $this->__get("border_left_style"),
  1063. "color" => $this->__get("border_left_color"),
  1064. ),
  1065. );
  1066. }
  1067. /**
  1068. * Return a single border property
  1069. *
  1070. * @param string $side
  1071. *
  1072. * @return mixed
  1073. */
  1074. protected function _get_border($side) {
  1075. $color = $this->__get("border_" . $side . "_color");
  1076. return $this->__get("border_" . $side . "_width") . " " .
  1077. $this->__get("border_" . $side . "_style") . " " . $color["hex"];
  1078. }
  1079. /**#@+
  1080. * Return full border properties as a string
  1081. *
  1082. * Border properties are returned just as specified in CSS:
  1083. * <pre>[width] [style] [color]</pre>
  1084. * e.g. "1px solid blue"
  1085. *
  1086. * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
  1087. * @return string
  1088. */
  1089. function get_border_top() {
  1090. return $this->_get_border("top");
  1091. }
  1092. function get_border_right() {
  1093. return $this->_get_border("right");
  1094. }
  1095. function get_border_bottom() {
  1096. return $this->_get_border("bottom");
  1097. }
  1098. function get_border_left() {
  1099. return $this->_get_border("left");
  1100. }
  1101. /**#@-*/
  1102. function get_computed_border_radius($w, $h) {
  1103. if ( !empty($this->_computed_border_radius) ) {
  1104. return $this->_computed_border_radius;
  1105. }
  1106. $rTL = $this->__get("border_top_left_radius");
  1107. $rTR = $this->__get("border_top_right_radius");
  1108. $rBL = $this->__get("border_bottom_left_radius");
  1109. $rBR = $this->__get("border_bottom_right_radius");
  1110. if ( $rTL + $rTR + $rBL + $rBR == 0 ) {
  1111. return $this->_computed_border_radius = array(
  1112. 0, 0, 0, 0,
  1113. "top-left" => 0,
  1114. "top-right" => 0,
  1115. "bottom-right" => 0,
  1116. "bottom-left" => 0,
  1117. );
  1118. }
  1119. $t = $this->__get("border_top_width");
  1120. $r = $this->__get("border_right_width");
  1121. $b = $this->__get("border_bottom_width");
  1122. $l = $this->__get("border_left_width");
  1123. $rTL = min($rTL, $h - $rBL - $t/2 - $b/2, $w - $rTR - $l/2 - $r/2);
  1124. $rTR = min($rTR, $h - $rBR - $t/2 - $b/2, $w - $rTL - $l/2 - $r/2);
  1125. $rBL = min($rBL, $h - $rTL - $t/2 - $b/2, $w - $rBR - $l/2 - $r/2);
  1126. $rBR = min($rBR, $h - $rTR - $t/2 - $b/2, $w - $rBL - $l/2 - $r/2);
  1127. return $this->_computed_border_radius = array(
  1128. $rTL, $rTR, $rBR, $rBL,
  1129. "top-left" => $rTL,
  1130. "top-right" => $rTR,
  1131. "bottom-right" => $rBR,
  1132. "bottom-left" => $rBL,
  1133. );
  1134. }
  1135. /**#@-*/
  1136. /**
  1137. * Returns the outline color as an array
  1138. *
  1139. * See {@link Style::get_color()}
  1140. *
  1141. * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
  1142. * @return array
  1143. */
  1144. function get_outline_color() {
  1145. if ( $this->_props["outline_color"] === "" ) {
  1146. //see __set and __get, on all assignments clear cache!
  1147. $this->_prop_cache["outline_color"] = null;
  1148. $this->_props["outline_color"] = $this->__get("color");
  1149. }
  1150. return $this->munge_color($this->_props["outline_color"]);
  1151. }
  1152. /**#@+
  1153. * Returns the outline width, as it is currently stored
  1154. * @return float|string
  1155. */
  1156. function get_outline_width() {
  1157. $style = $this->__get("outline_style");
  1158. return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0;
  1159. }
  1160. /**#@+
  1161. * Return full outline properties as a string
  1162. *
  1163. * Outline properties are returned just as specified in CSS:
  1164. * <pre>[width] [style] [color]</pre>
  1165. * e.g. "1px solid blue"
  1166. *
  1167. * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
  1168. * @return string
  1169. */
  1170. function get_outline() {
  1171. $color = $this->__get("outline_color");
  1172. return
  1173. $this->__get("outline_width") . " " .
  1174. $this->__get("outline_style") . " " .
  1175. $color["hex"];
  1176. }
  1177. /**#@-*/
  1178. /**
  1179. * Returns border spacing as an array
  1180. *
  1181. * The array has the format (h_space,v_space)
  1182. *
  1183. * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
  1184. * @return array
  1185. */
  1186. function get_border_spacing() {
  1187. $arr = explode(" ", $this->_props["border_spacing"]);
  1188. if ( count($arr) == 1 ) {
  1189. $arr[1] = $arr[0];
  1190. }
  1191. return $arr;
  1192. }
  1193. /*==============================*/
  1194. /*
  1195. !important attribute
  1196. For basic functionality of the !important attribute with overloading
  1197. of several styles of an element, changes in inherit(), merge() and _parse_properties()
  1198. are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()]
  1199. Only for combined attributes extra treatment needed. See below.
  1200. div { border: 1px red; }
  1201. div { border: solid; } // Not combined! Only one occurence of same style per context
  1202. //
  1203. div { border: 1px red; }
  1204. div a { border: solid; } // Adding to border style ok by inheritance
  1205. //
  1206. div { border-style: solid; } // Adding to border style ok because of different styles
  1207. div { border: 1px red; }
  1208. //
  1209. div { border-style: solid; !important} // border: overrides, even though not !important
  1210. div { border: 1px dashed red; }
  1211. //
  1212. div { border: 1px red; !important }
  1213. div a { border-style: solid; } // Need to override because not set
  1214. Special treatment:
  1215. At individual property like border-top-width need to check whether overriding value is also !important.
  1216. Also store the !important condition for later overrides.
  1217. Since not known who is initiating the override, need to get passed !important as parameter.
  1218. !important Paramter taken as in the original style in the css file.
  1219. When property border !important given, do not mark subsets like border_style as important. Only
  1220. individual properties.
  1221. Note:
  1222. Setting individual property directly from css with e.g. set_border_top_style() is not needed, because
  1223. missing set funcions handled by a generic handler __set(), including the !important.
  1224. Setting individual property of as sub-property is handled below.
  1225. Implementation see at _set_style_side_type()
  1226. Callers _set_style_sides_type(), _set_style_type, _set_style_type_important()
  1227. Related functionality for background, padding, margin, font, list_style
  1228. */
  1229. /* Generalized set function for individual attribute of combined style.
  1230. * With check for !important
  1231. * Applicable for background, border, padding, margin, font, list_style
  1232. * Note: $type has a leading underscore (or is empty), the others not.
  1233. */
  1234. protected function _set_style_side_type($style, $side, $type, $val, $important) {
  1235. $prop = $style.'_'.$side.$type;
  1236. if ( !isset($this->_important_props[$prop]) || $important) {
  1237. //see __set and __get, on all assignments clear cache!
  1238. $this->_prop_cache[$prop] = null;
  1239. if ( $important ) {
  1240. $this->_important_props[$prop] = true;
  1241. }
  1242. $this->_props[$prop] = $val;
  1243. }
  1244. }
  1245. protected function _set_style_sides_type($style,$top,$right,$bottom,$left,$type,$important) {
  1246. $this->_set_style_side_type($style,'top',$type,$top,$important);
  1247. $this->_set_style_side_type($style,'right',$type,$right,$important);
  1248. $this->_set_style_side_type($style,'bottom',$type,$bottom,$important);
  1249. $this->_set_style_side_type($style,'left',$type,$left,$important);
  1250. }
  1251. protected function _set_style_type($style,$type,$val,$important) {
  1252. $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
  1253. $arr = explode(" ", $val);
  1254. switch (count($arr)) {
  1255. case 1: $this->_set_style_sides_type($style,$arr[0],$arr[0],$arr[0],$arr[0],$type,$important); break;
  1256. case 2: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[0],$arr[1],$type,$important); break;
  1257. case 3: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[1],$type,$important); break;
  1258. case 4: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[3],$type,$important); break;
  1259. }
  1260. //see __set and __get, on all assignments clear cache!
  1261. $this->_prop_cache[$style.$type] = null;
  1262. $this->_props[$style.$type] = $val;
  1263. }
  1264. protected function _set_style_type_important($style,$type,$val) {
  1265. $this->_set_style_type($style,$type,$val,isset($this->_important_props[$style.$type]));
  1266. }
  1267. /* Anyway only called if _important matches and is assigned
  1268. * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side]));
  1269. */
  1270. protected function _set_style_side_width_important($style,$side,$val) {
  1271. //see __set and __get, on all assignments clear cache!
  1272. $this->_prop_cache[$style.'_'.$side] = null;
  1273. $this->_props[$style.'_'.$side] = str_replace("none", "0px", $val);
  1274. }
  1275. protected function _set_style($style,$val,$important) {
  1276. if ( !isset($this->_important_props[$style]) || $important) {
  1277. if ( $important ) {
  1278. $this->_important_props[$style] = true;
  1279. }
  1280. //see __set and __get, on all assignments clear cache!
  1281. $this->_prop_cache[$style] = null;
  1282. $this->_props[$style] = $val;
  1283. }
  1284. }
  1285. protected function _image($val) {
  1286. $DEBUGCSS=DEBUGCSS;
  1287. $parsed_url = "none";
  1288. if ( mb_strpos($val, "url") === false ) {
  1289. $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
  1290. }
  1291. else {
  1292. $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
  1293. // Resolve the url now in the context of the current stylesheet
  1294. $parsed_url = explode_url($val);
  1295. if ( $parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "" ) {
  1296. if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) {
  1297. $path = $_SERVER["DOCUMENT_ROOT"].'/';
  1298. }
  1299. else {
  1300. $path = $this->_stylesheet->get_base_path();
  1301. }
  1302. $path .= $parsed_url["path"] . $parsed_url["file"];
  1303. $path = realpath($path);
  1304. // If realpath returns FALSE then specifically state that there is no background image
  1305. if ( !$path ) {
  1306. $path = 'none';
  1307. }
  1308. }
  1309. else {
  1310. $path = build_url($this->_stylesheet->get_protocol(),
  1311. $this->_stylesheet->get_host(),
  1312. $this->_stylesheet->get_base_path(),
  1313. $val);
  1314. }
  1315. }
  1316. if ($DEBUGCSS) {
  1317. print "<pre>[_image\n";
  1318. print_r($parsed_url);
  1319. print $this->_stylesheet->get_protocol()."\n".$this->_stylesheet->get_base_path()."\n".$path."\n";
  1320. print "_image]</pre>";;
  1321. }
  1322. return $path;
  1323. }
  1324. /*======================*/
  1325. /**
  1326. * Sets color
  1327. *
  1328. * The color parameter can be any valid CSS color value
  1329. *
  1330. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
  1331. * @param string $color
  1332. */
  1333. function set_color($color) {
  1334. $col = $this->munge_color($color);
  1335. if ( is_null($col) || !isset($col["hex"]) ) {
  1336. $color = "inherit";
  1337. }
  1338. else {
  1339. $color = $col["hex"];
  1340. }
  1341. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1342. $this->_prop_cache["color"] = null;
  1343. $this->_props["color"] = $color;
  1344. }
  1345. /**
  1346. * Sets the background color
  1347. *
  1348. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
  1349. * @param string $color
  1350. */
  1351. function set_background_color($color) {
  1352. $col = $this->munge_color($color);
  1353. if ( is_null($col) ) {
  1354. return;
  1355. //$col = self::$_defaults["background_color"];
  1356. }
  1357. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1358. $this->_prop_cache["background_color"] = null;
  1359. $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col;
  1360. }
  1361. /**
  1362. * Set the background image url
  1363. * @link http://www.w3.org/TR/CSS21/colors.html#background-properties
  1364. *
  1365. * @param string $val
  1366. */
  1367. function set_background_image($val) {
  1368. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1369. $this->_prop_cache["background_image"] = null;
  1370. $this->_props["background_image"] = $this->_image($val);
  1371. }
  1372. /**
  1373. * Sets the background repeat
  1374. *
  1375. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
  1376. * @param string $val
  1377. */
  1378. function set_background_repeat($val) {
  1379. if ( is_null($val) ) {
  1380. $val = self::$_defaults["background_repeat"];
  1381. }
  1382. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1383. $this->_prop_cache["background_repeat"] = null;
  1384. $this->_props["background_repeat"] = $val;
  1385. }
  1386. /**
  1387. * Sets the background attachment
  1388. *
  1389. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
  1390. * @param string $val
  1391. */
  1392. function set_background_attachment($val) {
  1393. if ( is_null($val) ) {
  1394. $val = self::$_defaults["background_attachment"];
  1395. }
  1396. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1397. $this->_prop_cache["background_attachment"] = null;
  1398. $this->_props["background_attachment"] = $val;
  1399. }
  1400. /**
  1401. * Sets the background position
  1402. *
  1403. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
  1404. * @param string $val
  1405. */
  1406. function set_background_position($val) {
  1407. if ( is_null($val) ) {
  1408. $val = self::$_defaults["background_position"];
  1409. }
  1410. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1411. $this->_prop_cache["background_position"] = null;
  1412. $this->_props["background_position"] = $val;
  1413. }
  1414. /**
  1415. * Sets the background - combined options
  1416. *
  1417. * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
  1418. * @param string $val
  1419. */
  1420. function set_background($val) {
  1421. $val = trim($val);
  1422. $important = isset($this->_important_props["background"]);
  1423. if ( $val === "none" ) {
  1424. $this->_set_style("background_image", "none", $important);
  1425. $this->_set_style("background_color", "transparent", $important);
  1426. }
  1427. else {
  1428. $pos = array();
  1429. $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
  1430. $tmp = preg_split("/\s+/", $tmp);
  1431. foreach($tmp as $attr) {
  1432. if ( mb_substr($attr, 0, 3) === "url" || $attr === "none" ) {
  1433. $this->_set_style("background_image", $this->_image($attr), $important);
  1434. }
  1435. elseif ( $attr === "fixed" || $attr === "scroll" ) {
  1436. $this->_set_style("background_attachment", $attr, $important);
  1437. }
  1438. elseif ( $attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat" ) {
  1439. $this->_set_style("background_repeat", $attr, $important);
  1440. }
  1441. elseif ( ($col = $this->munge_color($attr)) != null ) {
  1442. $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important);
  1443. }
  1444. else {
  1445. $pos[] = $attr;
  1446. }
  1447. }
  1448. if (count($pos)) {
  1449. $this->_set_style("background_position", implode(" ", $pos), $important);
  1450. }
  1451. }
  1452. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1453. $this->_prop_cache["background"] = null;
  1454. $this->_props["background"] = $val;
  1455. }
  1456. /**
  1457. * Sets the font size
  1458. *
  1459. * $size can be any acceptable CSS size
  1460. *
  1461. * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
  1462. * @param string|float $size
  1463. */
  1464. function set_font_size($size) {
  1465. $this->__font_size_calculated = false;
  1466. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1467. $this->_prop_cache["font_size"] = null;
  1468. $this->_props["font_size"] = $size;
  1469. }
  1470. /**
  1471. * Sets the font style
  1472. *
  1473. * combined attributes
  1474. * set individual attributes also, respecting !important mark
  1475. * exactly this order, separate by space. Multiple fonts separated by comma:
  1476. * font-style, font-variant, font-weight, font-size, line-height, font-family
  1477. *
  1478. * Other than with border and list, existing partial attributes should
  1479. * reset when starting here, even when not mentioned.
  1480. * If individual attribute is !important and explicite or implicite replacement is not,
  1481. * keep individual attribute
  1482. *
  1483. * require whitespace as delimiters for single value attributes
  1484. * On delimiter "/" treat first as font height, second as line height
  1485. * treat all remaining at the end of line as font
  1486. * font-style, font-variant, font-weight, font-size, line-height, font-family
  1487. *
  1488. * missing font-size and font-family might be not allowed, but accept it here and
  1489. * use default (medium size, enpty font name)
  1490. *
  1491. * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
  1492. * @param $val
  1493. */
  1494. function set_font($val) {
  1495. $this->__font_size_calculated = false;
  1496. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1497. $this->_prop_cache["font"] = null;
  1498. $this->_props["font"] = $val;
  1499. $important = isset($this->_important_props["font"]);
  1500. if ( preg_match("/^(italic|oblique|normal)\s*(.*)$/i",$val,$match) ) {
  1501. $this->_set_style("font_style", $match[1], $important);
  1502. $val = $match[2];
  1503. }
  1504. else {
  1505. $this->_set_style("font_style", self::$_defaults["font_style"], $important);
  1506. }
  1507. if ( preg_match("/^(small-caps|normal)\s*(.*)$/i",$val,$match) ) {
  1508. $this->_set_style("font_variant", $match[1], $important);
  1509. $val = $match[2];
  1510. }
  1511. else {
  1512. $this->_set_style("font_variant", self::$_defaults["font_variant"], $important);
  1513. }
  1514. //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip!
  1515. if ( preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) &&
  1516. !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/",$match[2])
  1517. ) {
  1518. $this->_set_style("font_weight", $match[1], $important);
  1519. $val = $match[2];
  1520. }
  1521. else {
  1522. $this->_set_style("font_weight", self::$_defaults["font_weight"], $important);
  1523. }
  1524. if ( preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i",$val,$match) ) {
  1525. $this->_set_style("font_size", $match[1], $important);
  1526. $val = $match[2];
  1527. if ( preg_match("/^\/\s*(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i", $val, $match ) ) {
  1528. $this->_set_style("line_height", $match[1], $important);
  1529. $val = $match[2];
  1530. }
  1531. else {
  1532. $this->_set_style("line_height", self::$_defaults["line_height"], $important);
  1533. }
  1534. }
  1535. else {
  1536. $this->_set_style("font_size", self::$_defaults["font_size"], $important);
  1537. $this->_set_style("line_height", self::$_defaults["line_height"], $important);
  1538. }
  1539. if( strlen($val) != 0 ) {
  1540. $this->_set_style("font_family", $val, $important);
  1541. }
  1542. else {
  1543. $this->_set_style("font_family", self::$_defaults["font_family"], $important);
  1544. }
  1545. }
  1546. /**#@+
  1547. * Sets page break properties
  1548. *
  1549. * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
  1550. * @param string $break
  1551. */
  1552. function set_page_break_before($break) {
  1553. if ( $break === "left" || $break === "right" ) {
  1554. $break = "always";
  1555. }
  1556. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1557. $this->_prop_cache["page_break_before"] = null;
  1558. $this->_props["page_break_before"] = $break;
  1559. }
  1560. function set_page_break_after($break) {
  1561. if ( $break === "left" || $break === "right" ) {
  1562. $break = "always";
  1563. }
  1564. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1565. $this->_prop_cache["page_break_after"] = null;
  1566. $this->_props["page_break_after"] = $break;
  1567. }
  1568. /**#@-*/
  1569. //........................................................................
  1570. /**#@+
  1571. * Sets the margin size
  1572. *
  1573. * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
  1574. * @param $val
  1575. */
  1576. function set_margin_top($val) {
  1577. $this->_set_style_side_width_important('margin','top',$val);
  1578. }
  1579. function set_margin_right($val) {
  1580. $this->_set_style_side_width_important('margin','right',$val);
  1581. }
  1582. function set_margin_bottom($val) {
  1583. $this->_set_style_side_width_important('margin','bottom',$val);
  1584. }
  1585. function set_margin_left($val) {
  1586. $this->_set_style_side_width_important('margin','left',$val);
  1587. }
  1588. function set_margin($val) {
  1589. $val = str_replace("none", "0px", $val);
  1590. $this->_set_style_type_important('margin','',$val);
  1591. }
  1592. /**#@-*/
  1593. /**#@+
  1594. * Sets the padding size
  1595. *
  1596. * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
  1597. * @param $val
  1598. */
  1599. function set_padding_top($val) {
  1600. $this->_set_style_side_width_important('padding','top',$val);
  1601. }
  1602. function set_padding_right($val) {
  1603. $this->_set_style_side_width_important('padding','right',$val);
  1604. }
  1605. function set_padding_bottom($val) {
  1606. $this->_set_style_side_width_important('padding','bottom',$val);
  1607. }
  1608. function set_padding_left($val) {
  1609. $this->_set_style_side_width_important('padding','left',$val);
  1610. }
  1611. function set_padding($val) {
  1612. $val = str_replace("none", "0px", $val);
  1613. $this->_set_style_type_important('padding','',$val);
  1614. }
  1615. /**#@-*/
  1616. /**
  1617. * Sets a single border
  1618. *
  1619. * @param string $side
  1620. * @param string $border_spec ([width] [style] [color])
  1621. * @param boolean $important
  1622. */
  1623. protected function _set_border($side, $border_spec, $important) {
  1624. $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec);
  1625. //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10)
  1626. $arr = explode(" ", $border_spec);
  1627. // FIXME: handle partial values
  1628. //For consistency of individal and combined properties, and with ie8 and firefox3
  1629. //reset all attributes, even if only partially given
  1630. $this->_set_style_side_type('border',$side,'_style',self::$_defaults['border_'.$side.'_style'],$important);
  1631. $this->_set_style_side_type('border',$side,'_width',self::$_defaults['border_'.$side.'_width'],$important);
  1632. $this->_set_style_side_type('border',$side,'_color',self::$_defaults['border_'.$side.'_color'],$important);
  1633. foreach ($arr as $value) {
  1634. $value = trim($value);
  1635. if ( in_array($value, self::$BORDER_STYLES) ) {
  1636. $this->_set_style_side_type('border',$side,'_style',$value,$important);
  1637. }
  1638. else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) {
  1639. $this->_set_style_side_type('border',$side,'_width',$value,$important);
  1640. }
  1641. else {
  1642. // must be color
  1643. $this->_set_style_side_type('border',$side,'_color',$value,$important);
  1644. }
  1645. }
  1646. //see __set and __get, on all assignments clear cache!
  1647. $this->_prop_cache['border_'.$side] = null;
  1648. $this->_props['border_'.$side] = $border_spec;
  1649. }
  1650. /**
  1651. * Sets the border styles
  1652. *
  1653. * @link http://www.w3.org/TR/CSS21/box.html#border-properties
  1654. * @param string $val
  1655. */
  1656. function set_border_top($val) {
  1657. $this->_set_border("top", $val, isset($this->_important_props['border_top']));
  1658. }
  1659. function set_border_right($val) {
  1660. $this->_set_border("right", $val, isset($this->_important_props['border_right']));
  1661. }
  1662. function set_border_bottom($val) {
  1663. $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom']));
  1664. }
  1665. function set_border_left($val) {
  1666. $this->_set_border("left", $val, isset($this->_important_props['border_left']));
  1667. }
  1668. function set_border($val) {
  1669. $important = isset($this->_important_props["border"]);
  1670. $this->_set_border("top", $val, $important);
  1671. $this->_set_border("right", $val, $important);
  1672. $this->_set_border("bottom", $val, $important);
  1673. $this->_set_border("left", $val, $important);
  1674. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1675. $this->_prop_cache["border"] = null;
  1676. $this->_props["border"] = $val;
  1677. }
  1678. function set_border_width($val) {
  1679. $this->_set_style_type_important('border','_width',$val);
  1680. }
  1681. function set_border_color($val) {
  1682. $this->_set_style_type_important('border','_color',$val);
  1683. }
  1684. function set_border_style($val) {
  1685. $this->_set_style_type_important('border','_style',$val);
  1686. }
  1687. /**
  1688. * Sets the border radius size
  1689. *
  1690. * http://www.w3.org/TR/css3-background/#corners
  1691. */
  1692. function set_border_top_left_radius($val) {
  1693. $this->_set_border_radius_corner($val, "top_left");
  1694. }
  1695. function set_border_top_right_radius($val) {
  1696. $this->_set_border_radius_corner($val, "top_right");
  1697. }
  1698. function set_border_bottom_left_radius($val) {
  1699. $this->_set_border_radius_corner($val, "bottom_left");
  1700. }
  1701. function set_border_bottom_right_radius($val) {
  1702. $this->_set_border_radius_corner($val, "bottom_right");
  1703. }
  1704. function set_border_radius($val) {
  1705. $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces
  1706. $arr = explode(" ", $val);
  1707. switch (count($arr)) {
  1708. case 1: $this->_set_border_radii($arr[0],$arr[0],$arr[0],$arr[0]); break;
  1709. case 2: $this->_set_border_radii($arr[0],$arr[1],$arr[0],$arr[1]); break;
  1710. case 3: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[1]); break;
  1711. case 4: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[3]); break;
  1712. }
  1713. }
  1714. protected function _set_border_radii($val1, $val2, $val3, $val4) {
  1715. $this->_set_border_radius_corner($val1, "top_left");
  1716. $this->_set_border_radius_corner($val2, "top_right");
  1717. $this->_set_border_radius_corner($val3, "bottom_right");
  1718. $this->_set_border_radius_corner($val4, "bottom_left");
  1719. }
  1720. protected function _set_border_radius_corner($val, $corner) {
  1721. $this->_has_border_radius = true;
  1722. //see __set and __get, on all assignments clear cache!
  1723. $this->_prop_cache["border_" . $corner . "_radius"] = null;
  1724. $this->_props["border_" . $corner . "_radius"] = $this->length_in_pt($val);
  1725. }
  1726. /**
  1727. * Sets the outline styles
  1728. *
  1729. * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
  1730. * @param string $val
  1731. */
  1732. function set_outline($val) {
  1733. $important = isset($this->_important_props["outline"]);
  1734. $props = array(
  1735. "outline_style",
  1736. "outline_width",
  1737. "outline_color",
  1738. );
  1739. foreach($props as $prop) {
  1740. $_val = self::$_defaults[$prop];
  1741. if ( !isset($this->_important_props[$prop]) || $important) {
  1742. //see __set and __get, on all assignments clear cache!
  1743. $this->_prop_cache[$prop] = null;
  1744. if ( $important ) {
  1745. $this->_important_props[$prop] = true;
  1746. }
  1747. $this->_props[$prop] = $_val;
  1748. }
  1749. }
  1750. $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
  1751. $arr = explode(" ", $val);
  1752. foreach ($arr as $value) {
  1753. $value = trim($value);
  1754. if ( in_array($value, self::$BORDER_STYLES) ) {
  1755. $this->set_outline_style($value);
  1756. }
  1757. else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) {
  1758. $this->set_outline_width($value);
  1759. }
  1760. else {
  1761. // must be color
  1762. $this->set_outline_color($value);
  1763. }
  1764. }
  1765. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1766. $this->_prop_cache["outline"] = null;
  1767. $this->_props["outline"] = $val;
  1768. }
  1769. function set_outline_width($val) {
  1770. $this->_set_style_type_important('outline','_width',$val);
  1771. }
  1772. function set_outline_color($val) {
  1773. $this->_set_style_type_important('outline','_color',$val);
  1774. }
  1775. function set_outline_style($val) {
  1776. $this->_set_style_type_important('outline','_style',$val);
  1777. }
  1778. /**
  1779. * Sets the border spacing
  1780. *
  1781. * @link http://www.w3.org/TR/CSS21/box.html#border-properties
  1782. * @param float $val
  1783. */
  1784. function set_border_spacing($val) {
  1785. $arr = explode(" ", $val);
  1786. if ( count($arr) == 1 ) {
  1787. $arr[1] = $arr[0];
  1788. }
  1789. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1790. $this->_prop_cache["border_spacing"] = null;
  1791. $this->_props["border_spacing"] = "$arr[0] $arr[1]";
  1792. }
  1793. /**
  1794. * Sets the list style image
  1795. *
  1796. * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
  1797. * @param $val
  1798. */
  1799. function set_list_style_image($val) {
  1800. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1801. $this->_prop_cache["list_style_image"] = null;
  1802. $this->_props["list_style_image"] = $this->_image($val);
  1803. }
  1804. /**
  1805. * Sets the list style
  1806. *
  1807. * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
  1808. * @param $val
  1809. */
  1810. function set_list_style($val) {
  1811. $important = isset($this->_important_props["list_style"]);
  1812. $arr = explode(" ", str_replace(",", " ", $val));
  1813. static $types = array(
  1814. "disc", "circle", "square",
  1815. "decimal-leading-zero", "decimal", "1",
  1816. "lower-roman", "upper-roman", "a", "A",
  1817. "lower-greek",
  1818. "lower-latin", "upper-latin",
  1819. "lower-alpha", "upper-alpha",
  1820. "armenian", "georgian", "hebrew",
  1821. "cjk-ideographic", "hiragana", "katakana",
  1822. "hiragana-iroha", "katakana-iroha", "none"
  1823. );
  1824. static $positions = array("inside", "outside");
  1825. foreach ($arr as $value) {
  1826. /* http://www.w3.org/TR/CSS21/generate.html#list-style
  1827. * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
  1828. */
  1829. if ( $value === "none" ) {
  1830. $this->_set_style("list_style_type", $value, $important);
  1831. $this->_set_style("list_style_image", $value, $important);
  1832. continue;
  1833. }
  1834. //On setting or merging or inheriting list_style_image as well as list_style_type,
  1835. //and url exists, then url has precedence, otherwise fall back to list_style_type
  1836. //Firefox is wrong here (list_style_image gets overwritten on explicite list_style_type)
  1837. //Internet Explorer 7/8 and dompdf is right.
  1838. if ( mb_substr($value, 0, 3) === "url" ) {
  1839. $this->_set_style("list_style_image", $this->_image($value), $important);
  1840. continue;
  1841. }
  1842. if ( in_array($value, $types) ) {
  1843. $this->_set_style("list_style_type", $value, $important);
  1844. }
  1845. else if ( in_array($value, $positions) ) {
  1846. $this->_set_style("list_style_position", $value, $important);
  1847. }
  1848. }
  1849. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1850. $this->_prop_cache["list_style"] = null;
  1851. $this->_props["list_style"] = $val;
  1852. }
  1853. function set_size($val) {
  1854. $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/";
  1855. $val = mb_strtolower($val);
  1856. if ( $val === "auto" ) {
  1857. return;
  1858. }
  1859. $parts = preg_split("/\s+/", $val);
  1860. $computed = array();
  1861. if ( preg_match($length_re, $parts[0]) ) {
  1862. $computed[] = $this->length_in_pt($parts[0]);
  1863. if ( isset($parts[1]) && preg_match($length_re, $parts[1]) ) {
  1864. $computed[] = $this->length_in_pt($parts[1]);
  1865. }
  1866. else {
  1867. $computed[] = $computed[0];
  1868. }
  1869. }
  1870. elseif ( isset(CPDF_Adapter::$PAPER_SIZES[$parts[0]]) ) {
  1871. $computed = array_slice(CPDF_Adapter::$PAPER_SIZES[$parts[0]], 2, 2);
  1872. if ( isset($parts[1]) && $parts[1] === "landscape" ) {
  1873. $computed = array_reverse($computed);
  1874. }
  1875. }
  1876. else {
  1877. return;
  1878. }
  1879. $this->_props["size"] = $computed;
  1880. }
  1881. /**
  1882. * Sets the CSS3 transform property
  1883. *
  1884. * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property
  1885. * @param string $val
  1886. */
  1887. function set_transform($val) {
  1888. $number = "\s*([^,\s]+)\s*";
  1889. $tr_value = "\s*([^,\s]+)\s*";
  1890. $angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
  1891. if ( !preg_match_all("/[a-z]+\([^\)]+\)/i", $val, $parts, PREG_SET_ORDER) ) {
  1892. return;
  1893. }
  1894. $functions = array(
  1895. //"matrix" => "\($number,$number,$number,$number,$number,$number\)",
  1896. "translate" => "\($tr_value(?:,$tr_value)?\)",
  1897. "translateX" => "\($tr_value\)",
  1898. "translateY" => "\($tr_value\)",
  1899. "scale" => "\($number(?:,$number)?\)",
  1900. "scaleX" => "\($number\)",
  1901. "scaleY" => "\($number\)",
  1902. "rotate" => "\($angle\)",
  1903. "skew" => "\($angle(?:,$angle)?\)",
  1904. "skewX" => "\($angle\)",
  1905. "skewY" => "\($angle\)",
  1906. );
  1907. $transforms = array();
  1908. foreach($parts as $part) {
  1909. $t = $part[0];
  1910. foreach($functions as $name => $pattern) {
  1911. if ( preg_match("/$name\s*$pattern/i", $t, $matches) ) {
  1912. $values = array_slice($matches, 1);
  1913. switch($name) {
  1914. // <angle> units
  1915. case "rotate":
  1916. case "skew":
  1917. case "skewX":
  1918. case "skewY":
  1919. foreach($values as $i => $value) {
  1920. if ( strpos($value, "rad") ) {
  1921. $values[$i] = rad2deg(floatval($value));
  1922. }
  1923. else {
  1924. $values[$i] = floatval($value);
  1925. }
  1926. }
  1927. switch($name) {
  1928. case "skew":
  1929. if ( !isset($values[1]) ) {
  1930. $values[1] = 0;
  1931. }
  1932. break;
  1933. case "skewX":
  1934. $name = "skew";
  1935. $values = array($values[0], 0);
  1936. break;
  1937. case "skewY":
  1938. $name = "skew";
  1939. $values = array(0, $values[0]);
  1940. break;
  1941. }
  1942. break;
  1943. // <translation-value> units
  1944. case "translate":
  1945. $values[0] = $this->length_in_pt($values[0], $this->width);
  1946. if ( isset($values[1]) ) {
  1947. $values[1] = $this->length_in_pt($values[1], $this->height);
  1948. }
  1949. else {
  1950. $values[1] = 0;
  1951. }
  1952. break;
  1953. case "translateX":
  1954. $name = "translate";
  1955. $values = array($this->length_in_pt($values[0], $this->width), 0);
  1956. break;
  1957. case "translateY":
  1958. $name = "translate";
  1959. $values = array(0, $this->length_in_pt($values[0], $this->height));
  1960. break;
  1961. // <number> units
  1962. case "scale":
  1963. if ( !isset($values[1]) ) {
  1964. $values[1] = $values[0];
  1965. }
  1966. break;
  1967. case "scaleX":
  1968. $name = "scale";
  1969. $values = array($values[0], 1.0);
  1970. break;
  1971. case "scaleY":
  1972. $name = "scale";
  1973. $values = array(1.0, $values[0]);
  1974. break;
  1975. }
  1976. $transforms[] = array(
  1977. $name,
  1978. $values,
  1979. );
  1980. }
  1981. }
  1982. }
  1983. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  1984. $this->_prop_cache["transform"] = null;
  1985. $this->_props["transform"] = $transforms;
  1986. }
  1987. function set__webkit_transform($val) {
  1988. $this->set_transform($val);
  1989. }
  1990. function set__webkit_transform_origin($val) {
  1991. $this->set_transform_origin($val);
  1992. }
  1993. /**
  1994. * Sets the CSS3 transform-origin property
  1995. *
  1996. * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
  1997. * @param string $val
  1998. */
  1999. function set_transform_origin($val) {
  2000. $values = preg_split("/\s+/", $val);
  2001. if ( count($values) === 0) {
  2002. return;
  2003. }
  2004. foreach($values as &$value) {
  2005. if ( in_array($value, array("top", "left")) ) {
  2006. $value = 0;
  2007. }
  2008. if ( in_array($value, array("bottom", "right")) ) {
  2009. $value = "100%";
  2010. }
  2011. }
  2012. if ( !isset($values[1]) ) {
  2013. $values[1] = $values[0];
  2014. }
  2015. //see __set and __get, on all assignments clear cache, not needed on direct set through __set
  2016. $this->_prop_cache["transform_origin"] = null;
  2017. $this->_props["transform_origin"] = $values;
  2018. }
  2019. protected function parse_image_resolution($val) {
  2020. // If exif data could be get:
  2021. // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
  2022. $re = '/^\s*(\d+|normal|auto)\s*$/';
  2023. if ( !preg_match($re, $val, $matches) ) {
  2024. return null;
  2025. }
  2026. return $matches[1];
  2027. }
  2028. // auto | normal | dpi
  2029. function set_background_image_resolution($val) {
  2030. $parsed = $this->parse_image_resolution($val);
  2031. $this->_prop_cache["background_image_resolution"] = null;
  2032. $this->_props["background_image_resolution"] = $parsed;
  2033. }
  2034. // auto | normal | dpi
  2035. function set_image_resolution($val) {
  2036. $parsed = $this->parse_image_resolution($val);
  2037. $this->_prop_cache["image_resolution"] = null;
  2038. $this->_props["image_resolution"] = $parsed;
  2039. }
  2040. function set__dompdf_background_image_resolution($val) {
  2041. $this->set_background_image_resolution($val);
  2042. }
  2043. function set__dompdf_image_resolution($val) {
  2044. $this->set_image_resolution($val);
  2045. }
  2046. function set_z_index($val) {
  2047. if ( round($val) != $val && $val !== "auto" ) {
  2048. return;
  2049. }
  2050. $this->_prop_cache["z_index"] = null;
  2051. $this->_props["z_index"] = $val;
  2052. }
  2053. function set_counter_increment($val) {
  2054. $val = trim($val);
  2055. $value = null;
  2056. if ( in_array($val, array("none", "inherit")) ) {
  2057. $value = $val;
  2058. }
  2059. else {
  2060. if ( preg_match_all("/(".self::CSS_IDENTIFIER.")(?:\s+(".self::CSS_INTEGER."))?/", $val, $matches, PREG_SET_ORDER) ){
  2061. $value = array();
  2062. foreach($matches as $match) {
  2063. $value[$match[1]] = isset($match[2]) ? $match[2] : 1;
  2064. }
  2065. }
  2066. }
  2067. $this->_prop_cache["counter_increment"] = null;
  2068. $this->_props["counter_increment"] = $value;
  2069. }
  2070. /**
  2071. * Generate a string representation of the Style
  2072. *
  2073. * This dumps the entire property array into a string via print_r. Useful
  2074. * for debugging.
  2075. *
  2076. * @return string
  2077. */
  2078. /*DEBUGCSS print: see below additional debugging util*/
  2079. function __toString() {
  2080. return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size),
  2081. $this->_props), true);
  2082. }
  2083. /*DEBUGCSS*/ function debug_print() {
  2084. /*DEBUGCSS*/ print "parent_font_size:".$this->_parent_font_size . ";\n";
  2085. /*DEBUGCSS*/ foreach($this->_props as $prop => $val ) {
  2086. /*DEBUGCSS*/ print $prop.':'.$val;
  2087. /*DEBUGCSS*/ if (isset($this->_important_props[$prop])) {
  2088. /*DEBUGCSS*/ print '!important';
  2089. /*DEBUGCSS*/ }
  2090. /*DEBUGCSS*/ print ";\n";
  2091. /*DEBUGCSS*/ }
  2092. /*DEBUGCSS*/ }
  2093. }