�PNG  IHDR����Q�gAMA�� �a cHRMz&�����u0�`:�p��Q<�bKGD�������gmIDATx���w�U���ﹻ�& �^C��X(�����J I@� ���"% (** B�X� �+*�i��"]j(IH�{~�R)��[��~��>h��{�}g�y�)I�$I��j��� ������.I�$I�$�ʊ�y@�}x�.�: �$I�$I��i}��V�Z�����PC)I�$I��F� ����^��0ʐJ�$I�$�Q^���}{�"���r�=��OzI��$gR�ZeC.�IO����vH eK��X� �����$IM�px��sk�.��쒷/��&���r�[޳����<���v|�� ��.I���~�)@������$�up��dY�R�����a�$I �|�M�.�e ��Jaֶ�pS�Y�R��6j��>h�%IR��ز�� �i�f&���u�J)������M�����$I vL���i���=�H;�7UJ�,�]�,X��$I��1��AҒ�J����$ X�Y� XzI��@G����N��ҥR���T)E��@��;��]K*�M�w;#�5_�wO�n~\ DC&�$(A�5 �R�R��FkvIR���}�l��!�RytRl;��~^Ƿ�Jj� �اy�뷦BZ��Jr����&ӥ�8�P�j��w~�vn�����v ���X���^�(I;�4�R=�P[�3]J�,�]ȏ�~��:�3�?��[��� ��a��&e)`�e*����P[�4]�T��=Cq�6�R[ ~ޤ����r�XR Հg(�t�_HZ�-Hg �M�$�ãm�L5�R� �u��k�*`%C-�E6/����%[�t X.{��8�P9Z�������.vk����XŐKj����gKZ��Hg�(����aK9ڦ��mKj��Ѻm�_ \�#�$5�,)- � �61eJ�,��5m|� ��r�'��=��� ��&ڡd���%-]J �on�� X���m|�{ ��R�Ҟ����e $eڧY X��Y�����rԮ-a�7�RK�6h���>n$5A�V�ڴ�i��*�ֆ�K)���mѦ���tm�r�1p| �q:흺,)O�����i��*�ֺ�K)���ܬ�֦����K-5�r�3�>0ԔHj��Jئ�EZ��j�,%��r�e��~�/��z��%j�V��M��ڸ�mr��t)��3]J�,���T ��K֦O�vԒg��i�i��*�����bK�i�NO~�%�P��W���0=�d��i�i�������2�t�J9��J����ݕ�{�7�"I P��9�JK�Tb�u,%��r���"�6�RKU��}�Ij�2����HK�Z�XJ�,妝�� X��Y�����rP��� ެ�2�4�c��%i��^���IK|.H�,%��r�b���:XR�l��1X��4Pe/`����x��&����P��8�Pj��28��M��z���s��x���2���r�\���zR��P�z�4J����}�y���P[g=�L)��� .Q[�6Rj�Wgp ��FI�H�*-`I�����M�RaK9T��X��c�q�����*I� y���[j��E>cw%��gL�R���ԕi�F�Cj�-��ď�a`������#e~���I�� j�,%��r�,)?[gp �FI˨���mn��WX#��>mʔ X�A��� �DZf9,�nKҲz�����I��Z�XJ�,�L#��k�i��P�z�4JZF�����,�I,`���61%�2s �$���,�VO��Ϛ2��/U��FJ�fy��7����K�> X�+�6� S�TX����Ie�����JI���Lz�M�fKm ��L�RaK9�%|��4p9L�w�JI��!`N����sia���zĔ)������%-� X��M���q�>�pk�$-$�Q���2����x#�N� ؎�-�QR��}ᶦHZ�ډ��)�J�,�l#��i@y�n3������L��N`���;�nڔ X�����u����X5��p��F)��m|��^�0(��>B�H���F9(c�զE��er��JI rg��7 ��4I@z�0\�JI��������i�䵙���RR��0�s;�$�s6eJ�,�`n �䂦���0�a�)S)�A������ ���1eJ�,堌#�6�35R��I�gpN��Hu������TH���_S���ԕqV�����e `� ��&S)���>�p;S$魁eKI����uX��`I�����4��춒�o}`m�$1"��:�PI���<[�v9�^�\p��TJj�r�iRŭ ��P{#�{R2,`)e-`mgj�~�1�ϣ�L��Kam�7�&U\j�/�3mJ�,�`F��;M��'�䱀�� .KR#��)y�h�Tq�;p���cK9(���q!w�?����u�RR,n.yw�*UXj#�\�]ɱ���(q�v2=R���q����f����B#i��Jm�m�L����<]�Y����͙�#�$5� ��u�TU�7��Ӧ�X�R+q�,`I}����qL�����'���`��6�K�ͷ�6���r�,�]����0S$-���� ��[RKR3���o��iRE����|�nӦ�X�R.�(i�:�L��D�L��TJj�Y��%o�:����)�����6���r�x���zҒ��q��TJj��h㞦I���.�$Y�R.ʼ�n�GZ�\ֿ��f:%5�5 ��I�˼!�6����dK�x��m��4E�"mG�_�� �s? �.e*��?L�RfK9��%�����q�#�uh$�)�i���3U�����L�RfK9yx��m܌b�j�����8��4���$�i��1U��^@Wbm��4uJ�,�����Ҫ�A�>���_Ij�?1�v�3�2����[�gL�R��D��9�6�o�Ta�R���׿��N7%�����L�2�� NT�,`)7�&�Ɲ��L�*꽙��yp���_$���M�2�#�A�����S�,`)7�$r��k�TA���29�_���Iy�e"�|/0�t)�$�n X�T2���`Y���J���;�6��J�x"�����.e�<�`�����$)� P�I$��5�V4]���29���SRI>�~�=@j�]��l�p�2`K9Jaai�^" Ԋ��29�O�RI%��:X�V5]J��m��N9���]�H;1U���C39���NI%���Xe78�t)a��;���O��i Ҙ�>X�t�"~G>�_mn:%���|~ޅ_�+]�$�o���)�@��ǀ{hgN;�IK�6�G&�rp�)�T2�i�୦K�Ju���v*���T��=�T��O�SV�>(��~D�>d�����m�,I*��Ɛ�:���R�#��ۙNI%��D>G��.n��$�o��;�+#��R�R��!��.e�U��˽���TRI�2��8�t)1L��WϚ>IJ�a3�oF��b��u&���:�tJ*��(F7��y�0�Z�R ^�p���'Ii� ����L�24x�| X��RI%�ۄ>S1]J�y��[z��L�$�adB7��.eh4��%���%�누>W�E���Tf+3�����IR:�I�3Xה)3אO�ۦSR�O'�ٺ�)S�}"��q�O��r[B7�ϙ.edG�)^E���TR"R��t��R�ݜh���0}������<�S����ɧx�.6,)�&���)SI�p��j�'I��?���A�L�"���L����.\TZV�N���!�'I�Y.��pAS����5}� �TRbNL�3��”�d�����b�e��)��4]��Mg/S���Z{ni����,)=k����Д�d��p��ǦO��uLb7�ߛ2%lO�}u�)���K��]le�T�P��j�eS(I�Z���ִ��R�^eJ%%*�/\ Ke�̈́�-O�M�$����|.5eJ��o�s)^]oJ%%,�㚃R� <�p��LS��� �*����`GӦ�tdt<�5���o'��/�6�٧�� _��BIJ�kH��_�6%d ���rQ�b�gZ%%n�ڍ9o1mj�U�g���JR>�L�F�VӦD�B^k_���J�Dj��\����=�L���S(I������v─a���T�eZ%e�U����A�M-�0;�~˃@�i��|l�� �@S���4y���7��2��>���sX-vA�}�ϛBI���!ݎߨ����W�l�*)3{'�Y|�iS�lEڻ(��5�����K��t��SI�$��Uv0��2���,~��ԩ~�x��;�P��4��ց��C�r�O%ty�n4��25:���KM��l�D� ^���4JR������xS��ه�F_}شJ�T�S��6uj�+ﷸk�$e���Z�O%�G�*^�V2�����u3E�Mj�3��k%)ok��I]d�T����)�UR�K���DS� �7�~�m@�TJR�~�荪�f�T"�֛L�� �\���s��M��� �-0��T �K�f�J��z+��n�إK�r� �L�����&j��(��)�[�E&I����� ߴ>e �FW�_�kJR�|!���O�:5�/2跌3��T-�'|�zX�� r�yp0��J����S ~^�F�>-�2�< �`*%�ZFP�)����bS��n"���L�� :)���+pʷf(pO���3��TMW$~����>@~ū:����TA�IsV�1}�S2�<���%��ޟ�M�?@�iT ,E�ū�oz%i�~��g�|`wS(�]�oȤ��8��)�$�� �� ntu`өe�`6y�Pl� Iz�MI{�ʣ�z����ʨ�� �)IZ�2��= ld:5+���請M�$-ї���;�U�>_���g��sY��$Á����N�5��W���z�W�fIZ��)�-��y�u�XI�fp���~S*IZ��dt�;�t�>K�ū��KR�|$���#Lc�Ԁ+2�\�;kJ��`]�Y���ǔ��M1B)��U�bG"IRߊ���<x����ܾ��ӔJ�������0�Z����=��'�Y��嵤����� Le�v�e�g��)�$��z���n����V-º����^�3Ւ�o�f��#0�Tfk�^�Z�s[�*I꯳3{���)�ˬ����W�4Ւ4 �Odp��bZ��R���S��|�*I� �����5��5�#��"�&�-IvT&��/��윚Ye:��i�$ �9��{�Lk�u�R���e�[��I~�_�\��ؠ%�>�GL�$iY�8 �9ܕ��"�S���`kS.I�l���C;Ҏ4���x&�>�u_0J��Lr����<��J�2�(^��$5��L� s�=Mg�V� �~�,Ij�u��>�� 7��r2�)^=G���$�1:��3�G< �`J�3�~�&IR%�� 6���T�x�/�rIj���3�O< �ʔ&#f�_yX��J�i�ގN��Sz;�� T�x�(��i���8%���#���4 �~�AS+Ij��e���r�I�U�rIj�����3�62��v8��8�5�+Ij�A�h�K__5���X��%�n�V%Iͳ-y��|7��XV��2��v4���fzo��_6��8���"�S/I-qbf��;� Lk��F�)K���SM�$���� Ms�>K� W�N���V����}�^`��-�큧3����2Œ�Vؙ�G��d��u�,^�^m�%��6���~��N�n�&�͓��3Œ�V�Z����MsRpfE�W���%I�wd���ǀ�Lm[��7���W&�bIR��L�@Q�|�)*�� ����i ImsI�����MmKm�y��V`�i�$��G+R� 0�t�V'����!���V�)֏���28����v���U�7͒�v���H����ꦼt���x�ꗞ�T ;S���}��7�M�f���+��fIR���H��N��ZUk�U�x5��SA�Jㄌ�9��Mq��μ���AIRi|�j�5��)o����*^���'<$�T����w���I�1��hE�U�^c�_�j�?���Е$%d`z� �c�y�f��,X���O IJ�nTg����A�� �����U�XRD��������� ���}�������{�H�}��^�S,P5��V��2���\����Xx`p�Z����|Y�k:����$e�� ��~ ��@nW�L�.j�+��ϝ���Y��b퇪��bZ� �BV�u�)�u�������/IJ_ �1�[�p.p60�bC�� >|X����9�1P�:��N\�!�5�qUB}5�a5ja `ub��c�VxYt1N�0�Z����z���l4����]7­gKj�]�?�4ϻ� ���*���[��b��g$)+À���*x쳀ogO$~,5� �ز���U��S�����9���� lq3�+5�mgw@��n�p1��sso Ӻ=����|�N6 /�g(�Wv7U��;��zωM=��wk�,0���u��T��g�_��`_�P`�uz?�2�yI��!b��`�k���ĸSo��+Q���x%!\��ο����e����|�އ���ԁK��S-s6��pu���_����(ֿ�$�i+��+�T8=�e�Y;��� �צ��P�+p��h�x��WQ���v���*���|p1��. ��ά. XRk�IQ�Y�P,���d�r�Z�� |����� ��������B�%������w��P|�S5`��~́@�i�޾��� E�;��Չaw{o'�Q��?%�iL{u D��?���������N1��B��D������!�o��w�����PHRe��FZ�*�� ���k�_-~����{����E9�b-��~P�`��f��E{AܶB�J�A�FO��� wx6��R�ox�5 K5����=�W����we�������hS8 (��J���C���l�J���~ p+���F���i�;ŗo+��:�bD�#g(��C��"�wA^�� r.�F�8L;�dzd�IH�U�X��݆�Ϟ�X�g� �)I�F����q��e�m��%I�4�d�j&pp�T�{���'{�HO���x�( Rk���6^C�٫����O.�)�3�:s(��۳(Z�?~ٻ8�9�zmT"�PL�tw䥈��5��&b<8GZ-�Y��&�K�?e8,`I��6���e�����(֍x�b8�3 � `���r�����zX�j��)F�=l($I�j� �2*�(F�?h(/9ik:��I`m#�p3�Mg���L�aKj�c�/U��#�n5����S�# �������m(^)=y=đ�x8Ŭ����I���[U]����~S�цA�4�p���$-F �i(��R�,�7C�x�;X��=�c����I��>���{���Km�\ �o(T��v�2������v�x�2q�i����iDJ�N�,���Ҏ����!1f� �5quB�j��1��!�8 �r���D�Fd(���!���W���Ql��,g�S��k����L��1Bx��g'�'�՞�^���ǘ;�p����Q ���P(c���_ IRu����j�g(�W�z ����b�s�#�P�­rz�>���� k�� c&nB=�q+��ؔX���n#r5����)co���*Ũ�+G��?7��<�� |�P�����Q��ӣ'�G�����`uO�d>%M�ct�z�#�� Ԫ�����ڞ�&�7��CaQ��~N�'��-P�.�W`Oedp0�3C!IZc�I�AMP��U�ۀ5�J�<��\u~+�{�9�(Fb�b���y�A�e�B����hOS���ܳ�1� b��È���T�#��ŠyDžs�����,`5�}��D���C�-�`��̞%r&�ڙa��8�7Q����W����W����p6e7� ��Rϫ/�o����Y� ꇅ N��ܶ�ը��tc��!��L���A ���T�7�V4�J�sū� I-�0����P��x�z7�QN�F���_�i����Z�g�úW�k�G���83� ���0e�Wr9 X����]㾮݁#���Jˢ C�}0��=3�ݱ��tB��i�]�_ ��&�{�{[/�o[�~ \q�鯜�0��0��٩���|��cD��3�=4��B_b� ���RY��b$ó�BR���sf�&������l�L�X#M*��C����_�L܄:gx�)WΘs���GSb���u��L ���rF$9�'�;\4�Ɍ�q�'�n�[%p.�Q`�������u ��h���N�b`eCQyQ|���l�_���C>L���b꟟3h��Sb�� �#��x��N��xS���������s^�� 88�|�Mz�)��}:�](vbۢ�amŖ࿥� ��0)Q����7���@�0���=?^k(*�J�����}�3ib�kF�n H�jB׻���NO���� �z�� �x}�7p 0�t��f����D����X�.lw��gȔ��h�Ծ�Ų� }6�g� E� |�Lk��LZ���t��eu+=���q���\I�v0쮑�)��Q�ٵpH8�/2?Σ�o�>�J�vpp��h�������o~�f>%bM��M���}���\�//��":�PT��c(v���9v���!��g�ո��Q ��)��U�fV��G��+!� ��35{=�x\�2�+��k�i,y$���~A1��iC�6#)v��C�5�^>�+gǵ�@1�Hy٪7����u;p ps�ϰ�u���/S�� <��aʸ����Gu't�D1�ԝI��<��p��g|����6�j��'p:�tպ�h��X�{�o(7v],��*�}��6�a_����<�u`��Ȯ�r.E�;ˑ�q�io�p�R��"������26�2E�8j�� ]����U��鿍ǜ�v���D��,2���վ�8ϫ�:�e/^AQ����T�H{�WgRl���̊���2Yx���"1�Q�> �wX�Rk,O�]�Lܳ���~V<�����F���8��a��_g~�o.�XCD�?S�t���h���梫A�o�%���~K1ݵ��O1�LyZ�bJ�� E��Q���xpq�i�Cpv��a6��_�:�wejT����]����"����<��u`"���� 2>���o4��5rp"N5k��;�m���{���rZ�b������Φ${#)��`(��Ŵ�g�,;j���%�6�j���.�pyYT��?}-��kB������D���c3q����A`��N��WQ���ū2�0�/^A��Z�W%�N�Q��� ��MI�.��X#P��#����,^Eb�c&��?X�R tA�V�|Y���.�1����!�����؅�⨉ccww���>���i��v��l(J��T�~� �u`��ٵDm �q)���+���Ri�� x/�x��8cyFO�!�/���*�!/��&��,7�<.���N���,�������YDŽ�&ܑ�Q�F1�Bz��)F���P�ʛ�?5����d� �6`�����kQձ �λc�؎�%58�2��Y��&nD�_$Je4��>a��?��!� ��ͨ�|�Ȏ�WZ��S�s��v���8� �j����(�I��&��y�j� �Jb5��m��?��H������Wp��=����g}�G��3��#�|I��,5v珿�] H~�R3�@B��������[☉9Ox~��oMy�=J���;�xUVoj�� �b�U�s�l_��35�t-�(Ճɼ�RB7�U!�q��c��+�x�4�H�_�Q�o֮$[���GO<��4`��&č�\GO�c[�.[*�A�f%m��G/� ň�M�/�r ��W�/Nw~B1U3������J�?��P&���Y�� �)`�ѓ����Z�����1���p]�^l“��W#)lWZ�i����l�U�Q�u`��-����m|xĐ,������_�ƪ|9i:�_��{*(3G�ѧ}�Uo�D+�>m_�?V��Pۅ�15���&}2�|���/p�IOʵ�>���� G�Z9�cmíت�mnz��)yߐb���D�������� �>e}:�)� �r|@�R5q�V�S�����A�10�C%�E�_��'^�8c��������R��7O;�6�[���eKeP�������G������ϦX7�j���b}��OT�GO^j��n*媓����7n����GMC�� � ���t,�k31�R�b �(v�yܴ�ʭ�!��iTh8~�ZY�Z�p��(q��s���RL ?�b���}����c�Ũ�ʊGO^���!��rP�JO��1��5�MJ[��c&~������Z`"��ѓޔ����H1���C&����^|��Ш|�rʼ,�A�wĴ?�����b��5)�t��L��U��)F�|�� �&��g٣O]���oqSU����j���y(��x<��Ϳ3 ���.���FS�k���oYg�2� \_#w��j�{u'r�Q������>���o���;���%n�|�F�*�O�_��L�"�e�9um��Dds�����?.��fu�u����Qb��IW�z |4\0� s�b;�O�v��xOS�s�; G%����T4g��FR�u�rj���(֍ڑb �u�ԖK�D���u��1MK{���1^ q;�� �C=�6\8��F��R��艇�!���%\Y�Ô�U| �88�m��)֓��Nc��L�ve�� C�6z;��o&�X x5�9�:q���6�1�Z��(T����7���>C?�g�c�ļ�x�ѐ�� Z� ���o�o-�0�8j�ہ �x�,�`���'��� ��Ҕ���Oc��Rl��f��~���`�����jj�"�.N�v+���sM������_��]������Z�k��� �g( UOP���������y�εx%�pU����h�2�������(���@��il0���ݽ��QXxp�px-�N�S��( W�O+�轾 n��Fߢ����3M��<;z�)��FBZ�j����c�i��u�/�Q�oF�� �7R�¥ Z��F�L�F�~��#����ȣ��ߨ^<쩡�ݛк���v�џ)��)���M��E>ώ�x4�m#!-�m���!L;vv#~Y[��đ��K�����m����x�9.[,��U����FS �����C���VkZ ���+���ߟ�r�Y٧��IZd/�io�i$�%��͝ب_ֶX�3���ܫ��hNU �� Z����Z�g�k�=���]��=������b���b��JS[�w��j�U(��)���*I =ώ:}-蹞�l�Uj�:��1��}����M�W��m�=̛���� _�� ¾,8��{__�����m{_�P��V���K^n3�e����sw5�ӫh�#�$-�q=�A̟> ,^I}P�^�J$�qY~Q[ Xq���9�<�r�d�sߏǜs�#������%/���y����kKZ�������b��?� S�k�tc�񫝶L���&I ���W!�b �>{#�&�T.^����G��Vj�_���_R��K�p����n,b=`�ż����Y@�^՝��;z�{p�aV��Kk����QXj�/�)y� ��TI�c&F�;FB�G�7w����g� ZZD�G��!����x�� �r_�t��Ƣ!�}�i�/�V��=M����/��#��n��B8 Xx�Ы ^�@�CR�<{䤭����Y��CN��)�e���K��OSƟa $��&�g[i3�.C�6x�rOc���8�TI���;�o�� ��hH6�P�&L{�@�q��6�[���� �G��zp�^���71�j��(�l�`�J�}]���e6�X����☉#͕� ���׈$A�B1�Vj��h㭦IRs��qFBj�w�Q_7�Xk��>y"������N=�M�B0� ��,�C #�o6MR��c���0��|�$�)�ف����"1����!i���xY<���B��9mx�� ��`���,��t�A�>)5ػ�Q���?j��Q�?�cn�>Y�Z�e�� �Tis���v���h�#�� �GMމȇ���p�:���ԴVuږ�8ɼH��]C.���5C!UV;F`m�b��Bk��� ��L�TM�vP���ʍϤj�?��ԯ/Q�r1���N�B`9s"����s�� �TYs����z��� ��&�9S%U԰�> �{��<�ؿ���SM���xB��|H�\3�@!U�|�� �k']������$U�+>��� |�HHM����Lޢ�?��V9i�D!-�@��x�� �TI���î�%�6Z��*��9X�@HMW#�?��n�N� ,o�e6�?tQw��ڱ�.�]-����y����'�:mW�0#!�J82qF�jH -`�ѓ�&��M��0����u� Uγmxϵ��^-��_�\�]����)@0R���t.8�/?ٰ�C��Y]�x���}=sD3�o��j�ަ���Ы�N���uS%U��}Ԥw��HH�>ڗ�jܷ_3gN �����q7�[q���2�l���a���*����A�r���Ǔ��Ԗ+p�8���/��R��GM�� ]j�a����c�d(�JhWko�6��ڎb�j�]i���5���Bj�����3+�3�!\j��1�����U�Z���L��s��L�T�v8�HHmup<��U���\��GMމ�3�R+��w4R�����6�j� XW�M�T!��u(�*!��Pz�,����#���Sq���*�8?vww )kO���a��$�[&��?�*�bB�X� �@���%�����8�]�=���R�r)kO��w�0j��i��M�Tq�� ng$�2\�q�8f�:���e�N1�R�xr<��5 ������;��M��p^�@;��7]R��ꎾ�JtER�.�/(5�v3�R[�@=�h�l����?����l�@;���.���[]�Q*�Z\�4��"1P�'Y��w��x��# ���ǀg���{��5�i��_IUR���z�RɞsyS5q�E����=�@�Y���っ �����v�k��6 ��&��5�1E�o0�|�kp�c��#�j=`�D���WRU�����j̟���J'P� �����w2 �S� �v�:��p�g�3Rv�},�#�����8�b��Z~�&��(F�=�i�><�>gK�M���Jj����0�@H%��,����W�΃�7�R) "�>c�,����� x�ix������ј���^ ��aܖ>�H[�i.UI�Hc �U�1=y�W\���=��S*���G���R~�)AF���=�`�&�����2��h`�D�z���T��󑓶�����J+����?�W+}��C�%��P:|�0H���܆��}-�<;O����C[�~o.��$~��i�}��~�HQ�� �Tv�X������Έ�r=b}$��v����i�z�L��4�:�ȰT|4�~����*��!o�X�QR6��L�k+�#������t/g �lԁߖ����[��Jڶ_N$�k���������*"�.� ���x���s��xX���7jRVbA��A�ʯKҎ��U3����)�zS�NN �_�'��s�?f����)��6������X���!%s�s�A���kʱ>���qƷ�b ��h�g� %n���� �~p�1RE��GM���HH�=�������B�Jiy[<�5 ���ǁJҖ�����g�K���R��*�倳��e��~�HUy��)A���g,K)`�V�w6bRR:��q����L#\�r���cl��K��/�$�s�h�*$�� ������6�����덤�� ����KԖc� 3��Z�9��=�Ɣ�=o>�X � �Ώ�"1�� )a��`�S��JJ�6�k<��U�������-]�� b�m`��{r�y;����T���u��_GR5���*�%6�do�#XRg#���-!nl��$u�3��A�� L+Q{��9�x~�a-�|�H� �vbq[\�NJT%�]���rO8,��E�-F�����w)+?(Y{�Lz�n6����׀��?C������R�~�,)m�䎧�R�7���cww����qpW�ڳ=i.��U`Xf�F�b=�V��LJ��H^LI}��%�} ��|��w���� aG�$�,��^�R^�6 k2�^B�{7��t�������V�%@G�q� p�%R�zģN_ ��HHI[7�ֱ�>(��<�c e�{%kϊ����P�+��SL'�T�cM���J����WR���m ���ŏ�"�w)qc e�f�꒵i?��b7�b����(�'�"��2r%��������~�HUS�1���\<��(`�1�W����x��9�=�8HY9��m:X��1�8�b��g�����D1��u ���~|H��;K��-��U�ep�,,� C�1 RV.���M�R�5�άh����,�t��W�O8W���C�$ XRV�sQS]3G�J|�1��2����� [�v�M� ������:��k�#����~tH�3�0Rf-�����HYݺ-`I�9�%l�I��D�T�m\ ����S�{]��9�gO���ڒ�M���NCV\��G��*����2���J�R�Ũ;�R��ҏ^���ڽ�̱�mq�1E�u?�To�3I���)��y^��#�j�J�w���^�Ń�j��^�v����vl����B_��⋌�P�4x>0$�c>���K†A�ļ9s_V���jT��t0l�#������m��>E��-�,�,�x�,��-�W��)������سo&�9�6�R�E XR.6b���Xw�+)G���A�����E�v�L�)�͞K4�$p=�Ũ��i_ѱ�O�j��b�� HY����/���+@�θH9޼]�N�ԥ��%n�{����� �&zjT�?�� �Ty) s^�U��L�����lb�,�P�iTf�^���<À�]������� ��62R^V��7)S!nl�l��S�6~�͝�V�}�-=%*� ʻ>���G�� ���������D��nK��<��y��&>L����Py7'r=Hj���� ���9��V`[c"�*��^�8H��pc�����O�8�b�nU�`4���J��ȪA�Ƌ#��1_\ XϘH��PR���gi�k(�~G�~��0��D���A���A����_2�p�|�J�묭a����2���\N�C�r�]����M���_0 �^T��%e#����vD��^��%��x��y-n���}�-E�\�3�aS%�yN!�r_��{ �)s���A��w ���ڼp1pEAk�~v�<�:`'ӭ^�5 �����A�r���X�������OI驻��T �(��dk�)�_�\<��w���^��W�I��"�RFj3��V# ���M<,o�J��.H��#�\�SK���s]���� )��9> P��u�������A�*�B�Y�]����y�B�"�l�\�ey��� ��hH���*t��b�K)3��� IK�Z��򹞋X�jN�� �n� �*n>k�]��X�_��d�!�ry��BH� � ]��*R�� ��0(#'�7 ������%es9??��ښFC��,ՁQP��������j�����AR��J�\Ρw� �K��#��j���ah�g�w�;�2$�l*�)� ��%���Xq5�!U᢯�6Re] |�0����[�_�����_64�c�h�&�_}��i�L8K��Eg�Ҏ�7 M��/�\`|.p,�~`�a���=�BR?x�ܐrQ���8K� XR���2M�8�f ?�`s�gW�S%�"� ������Ԉ 7R%���$� N������}��?QL1|-э�ټwI�Z��%���pv�L���3Hk>,I����m�g�W���7{��E�� x�PHx�7�3R�����A�� ���@R�S�� CC���� �������!\ȟ���5I��XR^Z���xHл�$Q[��ŝ�40 (�>�+� �_C ���>���BR�t��<,T�r�T �������{���O�����/�H��+˟Pl6 I ���B)/�V���C��<6���a��2����~�����(�XwV4�g�n���XR� ϱ5�ǀHٻ?tw�똤Eyxp���{�#���WK��� �q����G%5���]�,���(�0ӈH����� HZ���])ג=K1j��&��G(FbM�@����)%�I` XR�����g ʔ�� KZ�G(v��P,�<`�[� K���n^ ��SJR���sAʠ�5xՅF`�0&R�b�V� ��t��x�:Ea�UE�/{�f��i�2;.I����A��wW8��/��t�T�x�A��GOo��N�?�G���}�l L�(���n�����`�Zv?���p�B��8K�_g�����I�+ܗ � #��i��?���ޙ�.��) p����$�u�tc �~DžfՈE�o3��l/)I-U�?a�ԅ�^��j�x�A�r����A�� ΧX��������}�DmZ@QLےbTXGd�.^|x�KHR{���|Ε�W_h]�� ���I��J`[�G9�{��)�.y�)�� ��<���D�*��zk�(ּ���Ya����O���8S����?��2-��� ������H13����#pK"���I`]`O� ��h�&=�S���F1Z�/Ie����D1R�W�a�"t'�x?!)Ou:��1 ��|��6��gt\s�����7�=�z_;�ؠ��>�0X Y��A1]q�p?�p�_���k+J*��Y�@HI>�^��?�g�t.06R�n ��,��`�� ��?)�;p pSF9����Z����X���L�����BJP�W���j���gQ|�&)7!�� Hj��Q��t���<| ؅��W�5 x �W��� �� HIz�Y���oV���M�G�P��� Hj��n`+�\�(d��N���W)F+I�rS�[���|��/a�����`K��|�ͻ�0Hj�{�R,���Q=��\� (F�}\�W�������R)A�g��SG`I��s�n���AR�=|�8�$}�G(v��C��$)s���� FBJ�?]�_�u XRv�ύ��6z�� �Ũ�G[��3��6-�T9�H��z��p����W�̞ú������� X�����g�큽�=�7C�u������fzI���$��)�k�i���^q��k��-) � ���0H*�N` �QZ��k�k]/���t���nn���sI�^Gu't=��7$�� Z;�{���8�^��jB��%� ��IItR�QS7�[�ϭ���3 �$�_���O�Q�J`7�!�]���W��"��W,)�����Iy �W��� �AJA�;K���WG��`IY�{8���k$I�$�^��%����9�.�^(`��N|���LJ�%�@�$I�}ֽp���=FB*�xN��=gI?Q{٥�4B)m���w �$I���gc~d��Z@G�9K��� X�?7)a�K�%�݅K�$IZ��-`I���p����C ������U��6�$I��\0��>!��9�k}��� Xa� ����II�S���0H�$I �H ?1R��.�Ч�j���:�4~R�w���@p�$I����r��A*�u��}��W�j�WFPJ����$I�➓/���6#!�� L�Ӿ��+ X36�x�8J �|+L;v���$I���o�4����3���0����1�R2�0��M� I�$-E}��@����,pS�^ޟR[���/����s¹'��0H�$IKyf��Ÿ���f�������VO�π�FT*�����a$I��>��H��e��~����V���Y/3�R�/�)��>d$I��>2��8`Cj���w�,n@�FU*�9tt�f$I��~<;��=�/4RD~����@��� X��-�ѕ�z��ἱI�$��:� ԍ��R �a�@��b X��{��+�Qx��u�q�$I�Л�z�o���� /~3\8����ڒ���4B������N7�$IҀ���j �V]n1�8H�$I��YFBj�3��̚�̵���ja ����p���p��� �$I���s/3R� Ӻ�-�Yj+L;�.0�R�́��I�$�A���v?� #!5�"��aʄ��j}���U���Km�ɽ��H�$Ij��C���Ys?h$I��Dl8�4��3���.��v�}���m���7�UiI=�&�=0L�g0$I��4���: ���emb�e��`���� e�Qbm�0u�? �$I�T!Sƍ'�-���s��v�)s#C��0�:�XB���2���a� w I�$�zbww�{�."p��Pz�O� �=�Ɔ�\����[� �����o($I������aw]�`���E���).K���v�i�:�L�*#gР7[��$I�����yG���PI=@��R� �4�y��R~�̮�´cg I�$I/<�t�P�ͽ ��h�Dg�o� 94����Z^k盇�΄8��I���56��^�W�$I�^�0���̜�N�?4*�H`237}g��+h���x�o��q)��SJ@p|�`�� �$I��%>������-�h���O�0e�O�>��\ԣNߌZ��D6��R�=K ����~n($I�$��y�3��D>o4�b#px�2���$��yڪt���z���W���~a�� �$I��~?�x<��e{W���g��ô�{�x$/�=�{t�G�0�7��e���a���B�$IҀ�yG��^S�卆�"puS��3��*�E=洣��,`9�>��'���Bww�pH�$IZ��ݑ�nC�㧄���Pc�_9��sO gw����J=l1�:mKB�>�����Ab<4L�p���$I�����b �o1Z���Q�@8�5�b�̍ S'�F���,�F��e���,^I�$Ij���E�dù{�l4� �8�Ys_�s� Z8.��x �m"+{~��?q,��Z D�!I�$��ϻ�'|X�h��B�)=��…'�]��M�>��5��� r�g���otԎ 獽�PH�$Ij����IP���hh)n#�cÔq���A'�ug5qw���U�&r�F|1��E%I�$%����]��!'�3�AFD/;C�k_`�9��� �v�!ٴt�PV�;��x`�'��*b�Qa� w I�$I�x�5� �����FC�3D����_��~��A�_�#O݆���Dv��V?<���q�w�+I�$I�{��=�Z�8"�.#RI���Y�yj���Ǫ����=f�D�l�9�%�M�,�����a8$I�$��Yw�i[�7�ݍFe�$�s��1��ՋBV�A?�`�]#!��oz����4zjLJ���o8$I�$%�@3j�A��a4��(�o�� �;�p,,dya�=��F9ً[��LS���PH�$IJ�Y�Љ+3��> 5"���3�9�aZ�<ñh!�{T�pB�G��k��j}��S�p��� �$I��lvF��.���F$I� ��z<� '\���K*qq��.f�<���2�Y�!�S"-\I�$I��Yw�č��jF$ w9��� \ߪB�.�1�v!Ʊ���?+��r�:�^�!I�$�BϹ�B� ����H��"�B�;L��'G[ 4�U�����#5>�੐�)|#�o0��aڱ���$I���>�}��k&�1`U#�V��?��Ys��V x���>�{t���1�[�I~D���&(I�$I/{��H�0fw�"�q"���y�%��4����� I�X�y�E~��M�3 8Xψ��L}q�������E�$I���[�> �nD�?~�s����f��� �����]o�΁� �cT��6"?'�_�Ἣ� �$I��>�~��.f�|'!������N�?�⟩����0��G KkX�Z�E��]�ޡ;�����/����&�?k�� O�ۘH�$IR��������ۀw�XӨ��<�7@��P��nS��04�a����Ӷ�p�.��:��@���\IWQ�J6�s�S%I�$���e��5��ڑ���v`�3:���x'�;��w��q_�vp�gHyX�Z� ��3�gЂ7{{���E�����uԹ�n�±��}�$I�$��������8t;b|��5��91n��ء����Q"�P������6���O�5�i���� }�i�R�̈́���%�Q�̄p!�I䮢�]��������O{�H�$IR�ϻ�9��s֧�� a=`-� aB\X��0"+5"C�1�H�b?߮����3x��3�&�g�ş�g��g����l��_���h�����Z^,`5�?���ߎ��vĸ%�̀M!�OZC2#0x ����LJ��0�� �G�w����$I�$I�}�<�{Eb�+y���;�iI,`����ܚ��F�����:�5��ܛ�A�8���-O�-|�8�K�7��s�|#�Z8�a&�>���<��a&����/V��tb�t��L��ʌI�$I�$I�$I�$I�$I�$IRj���D��D�%tEXtdate:create2022-05-31T04:40:26+00:00�!Î%tEXtdate:modify2022-05-31T04:40:26+00:00�|{2IEND�B`�Mini Shell

HOME


Mini Shell 1.0
DIR:/home/drishtigems.com/www/vendor/dompdf/dompdf/src/Css/
Upload File :
Current File : /home/drishtigems.com/www/vendor/dompdf/dompdf/src/Css/Style.php
<?php
/**
 * @package dompdf
 * @link    http://dompdf.github.com/
 * @author  Benj Carson <benjcarson@digitaljunkies.ca>
 * @author  Helmut Tischer <htischer@weihenstephan.org>
 * @author  Fabien Ménager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace Dompdf\Css;

use Dompdf\Adapter\CPDF;
use Dompdf\Exception;
use Dompdf\FontMetrics;
use Dompdf\Frame;

/**
 * Represents CSS properties.
 *
 * The Style class is responsible for handling and storing CSS properties.
 * It includes methods to resolve colors and lengths, as well as getters &
 * setters for many CSS properties.
 *
 * Actual CSS parsing is performed in the {@link Stylesheet} class.
 *
 * @package dompdf
 */
class Style
{
    const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
    const CSS_INTEGER = "[+-]?\d+";
    const CSS_NUMBER = "[+-]?\d*\.?\d+";

    /**
     * Default font size, in points.
     *
     * @var float
     */
    static $default_font_size = 12;

    /**
     * Default line height, as a fraction of the font size.
     *
     * @var float
     */
    static $default_line_height = 1.2;

    /**
     * Default "absolute" font sizes relative to the default font-size
     * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property
     * @var array<float>
     */
    static $font_size_keywords = [
        "xx-small" => 0.6, // 3/5
        "x-small" => 0.75, // 3/4
        "small" => 0.889, // 8/9
        "medium" => 1, // 1
        "large" => 1.2, // 6/5
        "x-large" => 1.5, // 3/2
        "xx-large" => 2.0, // 2/1
    ];

    /**
     * List of valid text-align keywords.  Should also really be a constant.
     *
     * @var array
     */
    static $text_align_keywords = ["left", "right", "center", "justify"];

    /**
     * List of valid vertical-align keywords.  Should also really be a constant.
     *
     * @var array
     */
    static $vertical_align_keywords = ["baseline", "bottom", "middle", "sub",
        "super", "text-bottom", "text-top", "top"];

    /**
     * List of all block-level (outer) display types.
     * * https://www.w3.org/TR/css-display-3/#display-type
     * * https://www.w3.org/TR/css-display-3/#block-level
     */
    public const BLOCK_LEVEL_TYPES = [
        "block",
        // "flow-root",
        "list-item",
        // "flex",
        // "grid",
        "table"
    ];

    /**
     * List of all inline-level (outer) display types.
     * * https://www.w3.org/TR/css-display-3/#display-type
     * * https://www.w3.org/TR/css-display-3/#inline-level
     */
    public const INLINE_LEVEL_TYPES = [
        "inline",
        "inline-block",
        // "inline-flex",
        // "inline-grid",
        "inline-table"
    ];

    /**
     * List of all table-internal (outer) display types.
     * * https://www.w3.org/TR/css-display-3/#layout-specific-display
     */
    public const TABLE_INTERNAL_TYPES = [
        "table-row-group",
        "table-header-group",
        "table-footer-group",
        "table-row",
        "table-cell",
        "table-column-group",
        "table-column",
        "table-caption"
    ];

    /**
     * List of all inline (inner) display types.  Should really be a constant.
     *
     * @var array
     */
    static $INLINE_TYPES = ["inline"];

    /**
     * List of all block (inner) display types.  Should really be a constant.
     *
     * @var array
     */
    static $BLOCK_TYPES = ["block", "inline-block", "table-cell", "list-item"];

    /**
     * List of all table (inner) display types.  Should really be a constant.
     *
     * @var array
     */
    static $TABLE_TYPES = ["table", "inline-table"];

    /**
     * Lookup table for valid display types. Initially computed from the
     * different constants.
     *
     * @var array
     */
    protected static $valid_display_types = [];

    /**
     * List of all positioned types.  Should really be a constant.
     *
     * @var array
     */
    static $POSITIONNED_TYPES = ["relative", "absolute", "fixed"];

    /**
     * List of valid border styles.  Should also really be a constant.
     *
     * @var array
     */
    static $BORDER_STYLES = ["none", "hidden", "dotted", "dashed", "solid",
        "double", "groove", "ridge", "inset", "outset"];

    /**
     * Map of CSS shorthand properties and their corresponding sub-properties.
     * The order of the sub-properties is relevant for the fallback getter,
     * which is used in case no specific getter method is defined.
     *
     * @var array
     */
    protected static $_props_shorthand = [
        "background" => [
            "background_image",
            "background_position",
            "background_size",
            "background_repeat",
            // "background_origin",
            // "background_clip",
            "background_attachment",
            "background_color"
        ],
        "border" => [
            "border_width",
            "border_style",
            "border_color"
        ],
        "border_top" => [
            "border_top_width",
            "border_top_style",
            "border_top_color"
        ],
        "border_right" => [
            "border_right_width",
            "border_right_style",
            "border_right_color"
        ],
        "border_bottom" => [
            "border_bottom_width",
            "border_bottom_style",
            "border_bottom_color"
        ],
        "border_left" => [
            "border_left_width",
            "border_left_style",
            "border_left_color"
        ],
        "border_width" => [
            "border_top_width",
            "border_right_width",
            "border_bottom_width",
            "border_left_width"
        ],
        "border_style" => [
            "border_top_style",
            "border_right_style",
            "border_bottom_style",
            "border_left_style"
        ],
        "border_color" => [
            "border_top_color",
            "border_right_color",
            "border_bottom_color",
            "border_left_color"
        ],
        "border_radius" => [
            "border_top_left_radius",
            "border_top_right_radius",
            "border_bottom_right_radius",
            "border_bottom_left_radius"
        ],
        "font" => [
            "font_family",
            "font_size",
            // "font_stretch",
            "font_style",
            "font_variant",
            "font_weight",
            "line_height"
        ],
        "list_style" => [
            "list_style_image",
            "list_style_position",
            "list_style_type"
        ],
        "margin" => [
            "margin_top",
            "margin_right",
            "margin_bottom",
            "margin_left"
        ],
        "padding" => [
            "padding_top",
            "padding_right",
            "padding_bottom",
            "padding_left"
        ],
        "outline" => [
            "outline_width",
            "outline_style",
            "outline_color"
        ]
    ];

    /**
     * Maps legacy property names to actual property names.
     *
     * @var array
     */
    protected static $_props_alias = [
        "word_wrap"                           => "overflow_wrap",
        "_dompdf_background_image_resolution" => "background_image_resolution",
        "_dompdf_image_resolution"            => "image_resolution",
        "_webkit_transform"                   => "transform",
        "_webkit_transform_origin"            => "transform_origin"
    ];

    /**
     * Default style values.
     *
     * @link http://www.w3.org/TR/CSS21/propidx.html
     *
     * @var array
     */
    protected static $_defaults = null;

    /**
     * List of inherited properties
     *
     * @link http://www.w3.org/TR/CSS21/propidx.html
     *
     * @var array
     */
    protected static $_inherited = null;

    /**
     * Caches method_exists result
     *
     * @var array<bool>
     */
    protected static $_methods_cache = [];

    /**
     * The stylesheet this style belongs to
     *
     * @var Stylesheet
     */
    protected $_stylesheet;

    /**
     * Media queries attached to the style
     *
     * @var array
     */
    protected $_media_queries;

    /**
     * Specified (or declared) values of the CSS properties.
     * https://www.w3.org/TR/css-cascade-3/#value-stages
     *
     * @var array
     */
    protected $_props = [];

    /**
     * Properties set by an `!important` declaration.
     *
     * @var array
     */
    protected $_important_props = [];

    /**
     * Computed values of the CSS properties.
     *
     * @var array
     */
    protected $_props_computed = [];

    /**
     * Used values of the CSS properties.
     *
     * @var array
     */
    protected $_prop_cache = [];

    protected static $_dependency_map = [
        "border_top_style" => [
            "border_top_width"
        ],
        "border_bottom_style" => [
            "border_bottom_width"
        ],
        "border_left_style" => [
            "border_left_width"
        ],
        "border_right_style" => [
            "border_right_width"
        ],
        "direction" => [
            "text_align"
        ],
        "font_size" => [
            "background_position",
            "background_size",
            "border_top_width",
            "border_right_width",
            "border_bottom_width",
            "border_left_width",
            "line_height",
            "margin_top",
            "margin_right",
            "margin_bottom",
            "margin_left",
            "outline_width",
            "outline_offset",
            "padding_top",
            "padding_right",
            "padding_bottom",
            "padding_left"
        ],
        "float" => [
            "display"
        ],
        "position" => [
            "display"
        ],
        "outline_style" => [
            "outline_width"
        ]
    ];

    /**
     * Lookup table for dependent properties. Initially computed from the
     * dependency map.
     *
     * @var array
     */
    protected static $_dependent_props = [];

    /**
     * Style of the parent element in document tree.
     *
     * @var Style
     */
    protected $parent_style;

    /**
     * @var Frame
     */
    protected $_frame;

    /**
     * The origin of the style
     *
     * @var int
     */
    protected $_origin = Stylesheet::ORIG_AUTHOR;

    // private members
    /**
     * The computed bottom spacing
     */
    private $_computed_bottom_spacing = null;

    /**
     * @var bool
     */
    private $has_border_radius_cache = null;

    /**
     * @var array
     */
    private $resolved_border_radius = null;

    /**
     * @var FontMetrics
     */
    private $fontMetrics;

    /**
     * Class constructor
     *
     * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
     * @param int $origin
     */
    public function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR)
    {
        $this->setFontMetrics($stylesheet->getFontMetrics());

        $this->_stylesheet = $stylesheet;
        $this->_media_queries = [];
        $this->_origin = $origin;
        $this->parent_style = null;

        if (!isset(self::$_defaults)) {

            // Shorthand
            $d =& self::$_defaults;

            // All CSS 2.1 properties, and their default values
            $d["azimuth"] = "center";
            $d["background_attachment"] = "scroll";
            $d["background_color"] = "transparent";
            $d["background_image"] = "none";
            $d["background_image_resolution"] = "normal";
            $d["background_position"] = "0% 0%";
            $d["background_repeat"] = "repeat";
            $d["background"] = "";
            $d["border_collapse"] = "separate";
            $d["border_color"] = "";
            $d["border_spacing"] = "0";
            $d["border_style"] = "";
            $d["border_top"] = "";
            $d["border_right"] = "";
            $d["border_bottom"] = "";
            $d["border_left"] = "";
            $d["border_top_color"] = "currentcolor";
            $d["border_right_color"] = "currentcolor";
            $d["border_bottom_color"] = "currentcolor";
            $d["border_left_color"] = "currentcolor";
            $d["border_top_style"] = "none";
            $d["border_right_style"] = "none";
            $d["border_bottom_style"] = "none";
            $d["border_left_style"] = "none";
            $d["border_top_width"] = "medium";
            $d["border_right_width"] = "medium";
            $d["border_bottom_width"] = "medium";
            $d["border_left_width"] = "medium";
            $d["border_width"] = "";
            $d["border_bottom_left_radius"] = "0";
            $d["border_bottom_right_radius"] = "0";
            $d["border_top_left_radius"] = "0";
            $d["border_top_right_radius"] = "0";
            $d["border_radius"] = "";
            $d["border"] = "";
            $d["bottom"] = "auto";
            $d["caption_side"] = "top";
            $d["clear"] = "none";
            $d["clip"] = "auto";
            $d["color"] = "#000000";
            $d["content"] = "normal";
            $d["counter_increment"] = "none";
            $d["counter_reset"] = "none";
            $d["cue_after"] = "none";
            $d["cue_before"] = "none";
            $d["cue"] = "";
            $d["cursor"] = "auto";
            $d["direction"] = "ltr";
            $d["display"] = "inline";
            $d["elevation"] = "level";
            $d["empty_cells"] = "show";
            $d["float"] = "none";
            $d["font_family"] = $stylesheet->get_dompdf()->getOptions()->getDefaultFont();
            $d["font_size"] = "medium";
            $d["font_style"] = "normal";
            $d["font_variant"] = "normal";
            $d["font_weight"] = "normal";
            $d["font"] = "";
            $d["height"] = "auto";
            $d["image_resolution"] = "normal";
            $d["left"] = "auto";
            $d["letter_spacing"] = "normal";
            $d["line_height"] = "normal";
            $d["list_style_image"] = "none";
            $d["list_style_position"] = "outside";
            $d["list_style_type"] = "disc";
            $d["list_style"] = "";
            $d["margin_right"] = "0";
            $d["margin_left"] = "0";
            $d["margin_top"] = "0";
            $d["margin_bottom"] = "0";
            $d["margin"] = "";
            $d["max_height"] = "none";
            $d["max_width"] = "none";
            $d["min_height"] = "auto";
            $d["min_width"] = "auto";
            $d["orphans"] = "2";
            $d["outline_color"] = "currentcolor"; // "invert" special color is not supported
            $d["outline_style"] = "none";
            $d["outline_width"] = "medium";
            $d["outline_offset"] = "0";
            $d["outline"] = "";
            $d["overflow"] = "visible";
            $d["overflow_wrap"] = "normal";
            $d["padding_top"] = "0";
            $d["padding_right"] = "0";
            $d["padding_bottom"] = "0";
            $d["padding_left"] = "0";
            $d["padding"] = "";
            $d["page_break_after"] = "auto";
            $d["page_break_before"] = "auto";
            $d["page_break_inside"] = "auto";
            $d["pause_after"] = "0";
            $d["pause_before"] = "0";
            $d["pause"] = "";
            $d["pitch_range"] = "50";
            $d["pitch"] = "medium";
            $d["play_during"] = "auto";
            $d["position"] = "static";
            $d["quotes"] = "auto";
            $d["richness"] = "50";
            $d["right"] = "auto";
            $d["size"] = "auto"; // @page
            $d["speak_header"] = "once";
            $d["speak_numeral"] = "continuous";
            $d["speak_punctuation"] = "none";
            $d["speak"] = "normal";
            $d["speech_rate"] = "medium";
            $d["stress"] = "50";
            $d["table_layout"] = "auto";
            $d["text_align"] = "";
            $d["text_decoration"] = "none";
            $d["text_indent"] = "0";
            $d["text_transform"] = "none";
            $d["top"] = "auto";
            $d["unicode_bidi"] = "normal";
            $d["vertical_align"] = "baseline";
            $d["visibility"] = "visible";
            $d["voice_family"] = "";
            $d["volume"] = "medium";
            $d["white_space"] = "normal";
            $d["widows"] = "2";
            $d["width"] = "auto";
            $d["word_spacing"] = "normal";
            $d["z_index"] = "auto";

            // CSS3
            $d["opacity"] = "1.0";
            $d["background_size"] = "auto auto";
            $d["transform"] = "none";
            $d["transform_origin"] = "50% 50%";

            // for @font-face
            $d["src"] = "";
            $d["unicode_range"] = "";

            // vendor-prefixed properties
            $d["_dompdf_keep"] = "";

            // Properties that inherit by default
            self::$_inherited = [
                "azimuth",
                "background_image_resolution",
                "border_collapse",
                "border_spacing",
                "caption_side",
                "color",
                "cursor",
                "direction",
                "elevation",
                "empty_cells",
                "font_family",
                "font_size",
                "font_style",
                "font_variant",
                "font_weight",
                "font",
                "image_resolution",
                "letter_spacing",
                "line_height",
                "list_style_image",
                "list_style_position",
                "list_style_type",
                "list_style",
                "orphans",
                "overflow_wrap",
                "pitch_range",
                "pitch",
                "quotes",
                "richness",
                "speak_header",
                "speak_numeral",
                "speak_punctuation",
                "speak",
                "speech_rate",
                "stress",
                "text_align",
                "text_indent",
                "text_transform",
                "visibility",
                "voice_family",
                "volume",
                "white_space",
                "widows",
                "word_spacing",
            ];

            // Compute dependent props from dependency map
            foreach (self::$_dependency_map as $props) {
                foreach ($props as $prop) {
                    self::$_dependent_props[$prop] = true;
                }
            }

            // Compute valid display-type lookup table
            self::$valid_display_types = [
                "none"                => true,
                "-dompdf-br"          => true,
                "-dompdf-image"       => true,
                "-dompdf-list-bullet" => true,
                "-dompdf-page"        => true
            ];
            foreach (self::BLOCK_LEVEL_TYPES as $val) {
                self::$valid_display_types[$val] = true;
            }
            foreach (self::INLINE_LEVEL_TYPES as $val) {
                self::$valid_display_types[$val] = true;
            }
            foreach (self::TABLE_INTERNAL_TYPES as $val) {
                self::$valid_display_types[$val] = true;
            }
        }
    }

    /**
     * "Destructor": forcibly free all references held by this object
     */
    function dispose()
    {
    }

    /**
     * @param $media_queries
     */
    function set_media_queries($media_queries)
    {
        $this->_media_queries = $media_queries;
    }

    /**
     * @return array|int
     */
    function get_media_queries()
    {
        return $this->_media_queries;
    }

    /**
     * @param Frame $frame
     */
    function set_frame(Frame $frame)
    {
        $this->_frame = $frame;
    }

    /**
     * @return Frame
     */
    function get_frame()
    {
        return $this->_frame;
    }

    /**
     * @param $origin
     */
    function set_origin($origin)
    {
        $this->_origin = $origin;
    }

    /**
     * @return int
     */
    function get_origin()
    {
        return $this->_origin;
    }

    /**
     * returns the {@link Stylesheet} this Style is associated with.
     *
     * @return Stylesheet
     */
    function get_stylesheet()
    {
        return $this->_stylesheet;
    }

    public function is_absolute(): bool
    {
        $position = $this->__get("position");
        return $position === "absolute" || $position === "fixed";
    }

    public function is_in_flow(): bool
    {
        $float = $this->__get("float");
        return $float === "none" && !$this->is_absolute();
    }

    /**
     * Converts any CSS length value into an absolute length in points.
     *
     * length_in_pt() takes a single length (e.g. '1em') or an array of
     * lengths and returns an absolute length.  If an array is passed, then
     * the return value is the sum of all elements. If any of the lengths
     * provided are "auto" or "none" then that value is returned.
     *
     * If a reference size is not provided, the current font size is used.
     *
     * @param float|string|array $length   The numeric length (or string measurement) or array of lengths to resolve.
     * @param float|null         $ref_size An absolute reference size to resolve percentage lengths.
     *
     * @return float|string
     */
    function length_in_pt($length, ?float $ref_size = null)
    {
        $font_size = $this->__get("font_size");
        $ref_size = $ref_size ?? $font_size;

        if (!is_array($length)) {
            $length = [$length];
        }

        $ret = 0.0;

        foreach ($length as $l) {
            if ($l === "auto" || $l === "none") {
                return $l;
            }

            // Assume numeric values are already in points
            if (is_numeric($l)) {
                $ret += (float) $l;
                continue;
            }

            $val = $this->single_length_in_pt((string) $l, $ref_size, $font_size);

            // FIXME: Using the ref size as fallback here currently ensures that
            // invalid widths or heights are treated as the corresponding
            // containing-block dimension, which can look like the declaration
            // is being ignored. Implement proper compute methods instead, and
            // fall back to 0 here
            $ret += $val ?? $ref_size;
        }

        return $ret;
    }

    /**
     * Convert a length declaration to pt.
     *
     * @param string     $l         The length declaration.
     * @param float      $ref_size  Reference size for percentage declarations.
     * @param float|null $font_size Font size for resolving font-size relative units.
     *
     * @return float|null The length in pt, or `null` for invalid declarations.
     */
    protected function single_length_in_pt(string $l, float $ref_size = 0, ?float $font_size = null): ?float
    {
        static $cache = [];

        $font_size = $font_size ?? $this->__get("font_size");

        $key = "$l/$ref_size/$font_size";

        if (isset($cache[$key])) {
            return $cache[$key];
        }

        if (is_numeric($l)) {
            // Legacy support for unitless values, not covered by spec. Might
            // want to restrict this to unitless `0` in the future
            $value = (float) $l;
        }

        elseif (($i = mb_stripos($l, "%")) !== false) {
            $value = (float)mb_substr($l, 0, $i) / 100 * $ref_size;
        }

        elseif (($i = mb_stripos($l, "px")) !== false) {
            $dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi();
            $value = ((float)mb_substr($l, 0, $i) * 72) / $dpi;
        }

        elseif (($i = mb_stripos($l, "pt")) !== false) {
            $value = (float)mb_substr($l, 0, $i);
        }

        elseif (($i = mb_stripos($l, "rem")) !== false) {
            $root_style = $this->_stylesheet->get_dompdf()->getTree()->get_root()->get_style();
            $root_font_size = $root_style === null || $root_style === $this
                ? $font_size
                : $root_style->font_size;
            $value = (float)mb_substr($l, 0, $i) * $root_font_size;
        }

        elseif (($i = mb_stripos($l, "em")) !== false) {
            $value = (float)mb_substr($l, 0, $i) * $font_size;
        }

        elseif (($i = mb_stripos($l, "cm")) !== false) {
            $value = (float)mb_substr($l, 0, $i) * 72 / 2.54;
        }

        elseif (($i = mb_stripos($l, "mm")) !== false) {
            $value = (float)mb_substr($l, 0, $i) * 72 / 25.4;
        }

        elseif (($i = mb_stripos($l, "ex")) !== false) {
            // FIXME: em:ex ratio?
            $value = (float)mb_substr($l, 0, $i) * $font_size / 2;
        }

        elseif (($i = mb_stripos($l, "in")) !== false) {
            $value = (float)mb_substr($l, 0, $i) * 72;
        }

        elseif (($i = mb_stripos($l, "pc")) !== false) {
            $value = (float)mb_substr($l, 0, $i) * 12;
        }

        else {
            // Invalid or unsupported declaration
            $value = null;
        }

        return $cache[$key] = $value;
    }


    /**
     * Resolve inherited property values using the provided parent style or the
     * default values, in case no parent style exists.
     *
     * https://www.w3.org/TR/css-cascade-3/#inheriting
     *
     * @param Style|null $parent
     *
     * @return Style
     */
    function inherit(?Style $parent = null)
    {
        $this->parent_style = $parent;

        // Clear the computed font size, as it might depend on the parent
        // font size
        unset($this->_props_computed["font_size"]);
        unset($this->_prop_cache["font_size"]);

        if ($parent) {
            foreach (self::$_inherited as $prop) {
                // For properties that inherit by default: When the cascade did
                // not result in a value, inherit the parent value. Inheritance
                // is handled via the specific sub-properties for shorthands
                if (isset($this->_props[$prop]) || isset(self::$_props_shorthand[$prop])) {
                    continue;
                }
    
                if (isset($parent->_props[$prop])) {
                    $parent_val = \array_key_exists($prop, $parent->_props_computed)
                        ? $parent->_props_computed[$prop]
                        : $parent->compute_prop($prop, $parent->_props[$prop]);
    
                    $this->_props[$prop] = $parent_val;
                    $this->_props_computed[$prop] = $parent_val;
                    $this->_prop_cache[$prop] = null;
                }
            }
        }

        foreach ($this->_props as $prop => $val) {
            if ($val === "inherit") {
                if ($parent && isset($parent->_props[$prop])) {
                    $parent_val = \array_key_exists($prop, $parent->_props_computed)
                        ? $parent->_props_computed[$prop]
                        : $parent->compute_prop($prop, $parent->_props[$prop]);

                    $this->_props[$prop] = $parent_val;
                    $this->_props_computed[$prop] = $parent_val;
                    $this->_prop_cache[$prop] = null;
                } else {
                    // Parent prop not set, use default
                    $this->_props[$prop] = self::$_defaults[$prop];
                    unset($this->_props_computed[$prop]);
                    unset($this->_prop_cache[$prop]);
                }
            }
        }

        return $this;
    }

    /**
     * Override properties in this style with those in $style
     *
     * @param Style $style
     */
    function merge(Style $style)
    {
        foreach ($style->_props as $prop => $val) {
            $important = isset($style->_important_props[$prop]);

            // `!important` declarations take precedence over normal ones
            if (!$important && isset($this->_important_props[$prop])) {
                continue;
            }

            $computed = \array_key_exists($prop, $style->_props_computed)
                ? $style->_props_computed[$prop]
                : $style->compute_prop($prop, $val);

            // Skip invalid declarations. Because styles are merged into an
            // initially empty style object during stylesheet loading, this
            // handles all invalid declarations
            if ($computed === null) {
                continue;
            }

            if ($important) {
                $this->_important_props[$prop] = true;
            }

            $this->_props[$prop] = $val;

            // Don't use the computed value for dependent properties; they will
            // be computed on-demand during inheritance or property access
            // instead
            if (isset(self::$_dependent_props[$prop])) {
                unset($this->_props_computed[$prop]);
                unset($this->_prop_cache[$prop]);
            } else {
                $this->_props_computed[$prop] = $computed;
                $this->_prop_cache[$prop] = null;
            }
        }
    }

    /**
     * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "alpha"=>alpha, "hex"=>"#rrggbb")
     * based on the provided CSS color value.
     *
     * @param string $color
     * @return array|string|null
     */
    function munge_color($color)
    {
        return Color::parse($color);
    }

    /**
     * @deprecated
     * @param string $prop
     */
    function important_set($prop)
    {
        $prop = str_replace("-", "_", $prop);
        $this->_important_props[$prop] = true;
    }

    /**
     * @deprecated
     * @param string $prop
     * @return bool
     */
    function important_get($prop)
    {
        return isset($this->_important_props[$prop]);
    }

    /**
     * Clear information about important declarations after the style has been
     * finalized during stylesheet loading.
     */
    public function clear_important(): void
    {
        $this->_important_props = [];
    }

    /**
     * Set the specified value of a property.
     *
     * Setting `$clear_dependencies` to `false` is useful for saving a bit of
     * unnecessary work while loading stylesheets.
     *
     * @param string $prop               The property to set.
     * @param mixed  $val                The value declaration.
     * @param bool   $important          Whether the declaration is important.
     * @param bool   $clear_dependencies Whether to clear computed values of dependent properties.
     */
    function set_prop(string $prop, $val, bool $important = false, bool $clear_dependencies = true): void
    {
        $prop = str_replace("-", "_", $prop);

        // Legacy property aliases
        if (isset(self::$_props_alias[$prop])) {
            $prop = self::$_props_alias[$prop];
        }

        if (!isset(self::$_defaults[$prop])) {
            global $_dompdf_warnings;
            $_dompdf_warnings[] = "'$prop' is not a recognized CSS property.";
            return;
        }

        if ($prop !== "content" && is_string($val) && mb_strpos($val, "url") === false && strlen($val) > 1) {
            $val = mb_strtolower(trim(str_replace(["\n", "\t"], [" "], $val)));
            $val = preg_replace("/([0-9]+) (pt|px|pc|rem|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
        }

        if (isset(self::$_props_shorthand[$prop])) {
            // Shorthand properties directly set their respective sub-properties
            // https://www.w3.org/TR/css-cascade-3/#shorthand
            if ($val === "initial" || $val === "inherit" || $val === "unset") {
                foreach (self::$_props_shorthand[$prop] as $sub_prop) {
                    $this->set_prop($sub_prop, $val, $important, $clear_dependencies);
                }
            } else {
                $method = "set_$prop";
    
                if (!isset(self::$_methods_cache[$method])) {
                    self::$_methods_cache[$method] = method_exists($this, $method);
                }
    
                if (self::$_methods_cache[$method]) {
                    $this->$method($val, $important);
                }
            }
        } else {
            // `!important` declarations take precedence over normal ones
            if (!$important && isset($this->_important_props[$prop])) {
                return;
            }

            if ($important) {
                $this->_important_props[$prop] = true;
            }

            // https://www.w3.org/TR/css-cascade-3/#inherit-initial
            if ($val === "unset") {
                $val = in_array($prop, self::$_inherited, true)
                    ? "inherit"
                    : "initial";
            }

            // https://www.w3.org/TR/css-cascade-3/#valdef-all-initial
            if ($val === "initial") {
                $val = self::$_defaults[$prop];
            }

            $this->_props[$prop] = $val;
            unset($this->_props_computed[$prop]);
            unset($this->_prop_cache[$prop]);

            if ($clear_dependencies) {
                // Clear the computed values of any dependent properties, so
                // they can be re-computed
                if (isset(self::$_dependency_map[$prop])) {
                    foreach (self::$_dependency_map[$prop] as $dependent) {
                        unset($this->_props_computed[$dependent]);
                        unset($this->_prop_cache[$dependent]);
                    }
                }

                // Clear border-radius cache on setting any border-radius
                // property
                if ($prop === "border_top_left_radius"
                    || $prop === "border_top_right_radius"
                    || $prop === "border_bottom_left_radius"
                    || $prop === "border_bottom_right_radius"
                ) {
                    $this->has_border_radius_cache = null;
                }
            }

            // FIXME: temporary hack around lack of persistence of base href for
            // URLs. Compute value immediately, before the original base href is
            // no longer available
            if ($prop === "background_image" || $prop === "list_style_image") {
                $this->compute_prop($prop, $val);
            }
        }
    }

    /**
     * Similar to __get() without storing the result. Useful for accessing
     * properties while loading stylesheets.
     *
     * @param string $prop
     *
     * @return mixed
     * @throws Exception
     */
    function get_prop(string $prop)
    {
        // Legacy property aliases
        if (isset(self::$_props_alias[$prop])) {
            $prop = self::$_props_alias[$prop];
        }

        if (!isset(self::$_defaults[$prop])) {
            throw new Exception("'$prop' is not a recognized CSS property.");
        }

        $method = "get_$prop";

        if (isset($this->_props_computed[$prop])) {
            if (method_exists($this, $method)) {
                return $this->$method();
            }

            return $this->_props_computed[$prop];
        }

        // Fall back on defaults if property is not set
        return $this->_props[$prop] ?? self::$_defaults[$prop];
    }

    /**
     * PHP5 overloaded setter
     *
     * This function along with {@link Style::__get()} permit a user of the
     * Style class to access any (CSS) property using the following syntax:
     * <code>
     *  Style->margin_top = "1em";
     *  echo (Style->margin_top);
     * </code>
     *
     * __set() automatically calls the provided set function, if one exists,
     * otherwise it sets the property directly.  Typically, __set() is not
     * called directly from outside of this class.
     *
     * On each modification clear cache to return accurate setting.
     * Also affects direct settings not using __set
     * For easier finding all assignments, attempted to allowing only explicite assignment:
     * Very many uses, e.g. AbstractFrameReflower.php -> for now leave as it is
     * function __set($prop, $val) {
     *   throw new Exception("Implicit replacement of assignment by __set.  Not good.");
     * }
     * function props_set($prop, $val) { ... }
     *
     * @param string $prop the property to set
     * @param mixed $val the value of the property
     *
     */
    function __set($prop, $val)
    {
        $this->set_prop($prop, $val);
    }

    /**
     * PHP5 overloaded getter
     * Along with {@link Style::__set()} __get() provides access to all CSS
     * properties directly.  Typically __get() is not called directly outside
     * of this class.
     * On each modification clear cache to return accurate setting.
     * Also affects direct settings not using __set
     *
     * @param string $prop
     *
     * @return mixed
     * @throws Exception
     */
    function __get($prop)
    {
        // Legacy property aliases
        if (isset(self::$_props_alias[$prop])) {
            $prop = self::$_props_alias[$prop];
        }

        if (!isset(self::$_defaults[$prop])) {
            throw new Exception("'$prop' is not a recognized CSS property.");
        }

        if (isset($this->_prop_cache[$prop])) {
            return $this->_prop_cache[$prop];
        }

        $method = "get_$prop";
    
        if (!isset(self::$_methods_cache[$method])) {
            self::$_methods_cache[$method] = method_exists($this, $method);
        }

        if (isset(self::$_props_shorthand[$prop])) {
            // Don't cache shorthand values, always use getter. If no dedicated
            // getter exists, use a simple fallback getter concatenating all
            // sub-property values
            if (self::$_methods_cache[$method]) {
                return $this->$method();
            } else {
                return implode(" ", array_map(function ($sub_prop) {
                    $val = $this->__get($sub_prop);
                    return is_array($val) ? implode(" ", $val) : $val;
                }, self::$_props_shorthand[$prop]));
            }
        } else {
            // Compute the value if needed
            if (!\array_key_exists($prop, $this->_props_computed)) {
                $val = $this->_props[$prop] ?? self::$_defaults[$prop];
                $this->compute_prop($prop, $val);
            }

            // Invalid declarations are skipped on style merge, but during
            // style parsing, styles might contain invalid declarations. Fall
            // back to the default value in that case
            $computed = $this->_props_computed[$prop]
                ?? $this->compute_prop($prop, self::$_defaults[$prop]);
            $used = self::$_methods_cache[$method]
                ? $this->$method()
                : $computed;

            $this->_prop_cache[$prop] = $used;
            return $used;
        }
    }

    /**
     * Experimental fast setter for used values.
     *
     * If a shorthand property is specified, all of its sub-properties are set
     * to the same value.
     *
     * @param string $prop
     * @param mixed  $val
     */
    function set_used(string $prop, $val): void
    {
        // Legacy property aliases
        if (isset(self::$_props_alias[$prop])) {
            $prop = self::$_props_alias[$prop];
        }

        if (!isset(self::$_defaults[$prop])) {
            throw new Exception("'$prop' is not a recognized CSS property.");
        }

        if (isset(self::$_props_shorthand[$prop])) {
            foreach (self::$_props_shorthand[$prop] as $sub_prop) {
                $this->set_used($sub_prop, $val);
            }
        } else {
            $this->_prop_cache[$prop] = $val;
        }
    }

    /**
     * @param string $prop The property to compute.
     * @param mixed  $val  The value to compute.
     *
     * @return mixed The computed value.
     */
    protected function compute_prop(string $prop, $val)
    {
        $this->_props_computed[$prop] = null;
        $this->_prop_cache[$prop] = null;

        $method = "set_$prop";

        if (!isset(self::$_methods_cache[$method])) {
            self::$_methods_cache[$method] = method_exists($this, $method);
        }

        // During style merge, the parent style is not available yet, so
        // temporarily use the initial value for `inherit` properties. The
        // keyword is properly resolved during inheritance
        if ($val === "inherit") {
            $val = self::$_defaults[$prop];
        }

        if (self::$_methods_cache[$method]) {
            $this->$method($val);
        } elseif ($val !== "") {
            $this->_props_computed[$prop] = $val;
        }

        return $this->_props_computed[$prop];
    }

    /**
     * @param float $cbw The width of the containing block.
     * @return float|null|string
     */
    function computed_bottom_spacing(float $cbw)
    {
        // Caching the bottom spacing independently of the given width is a bit
        // iffy, but should be okay, as the containing block should only
        // potentially change after a page break, and the style is reset in that
        // case
        if ($this->_computed_bottom_spacing !== null) {
            return $this->_computed_bottom_spacing;
        }
        return $this->_computed_bottom_spacing = $this->length_in_pt(
            [
                $this->margin_bottom,
                $this->padding_bottom,
                $this->border_bottom_width
            ],
            $cbw
        );
    }

    /**
     * @return string
     */
    function get_font_family_raw()
    {
        return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
    }

    /**
     * Getter for the 'font-family' CSS property.
     * Uses the {@link FontMetrics} class to resolve the font family into an
     * actual font file.
     *
     * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
     * @throws Exception
     *
     * @return string
     */
    function get_font_family()
    {
        //TODO: we should be using the calculated prop rather than perform the entire family parsing operation again

        $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss();

        // Select the appropriate font.  First determine the subtype, then check
        // the specified font-families for a candidate.

        // Resolve font-weight
        $weight = $this->__get("font_weight");
        if ($weight === 'bold') {
            $weight = 700;
        } elseif (preg_match('/^[0-9]+$/', $weight, $match)) {
            $weight = (int)$match[0];
        } else {
            $weight = 400;
        }

        // Resolve font-style
        $font_style = $this->__get("font_style");
        $subtype = $this->getFontMetrics()->getType($weight . ' ' . $font_style);

        $families = preg_split("/\s*,\s*/", $this->_props_computed["font_family"]);

        $font = null;
        foreach ($families as $family) {
            //remove leading and trailing string delimiters, e.g. on font names with spaces;
            //remove leading and trailing whitespace
            $family = trim($family, " \t\n\r\x0B\"'");
            if ($DEBUGCSS) {
                print '(' . $family . ')';
            }
            $font = $this->getFontMetrics()->getFont($family, $subtype);

            if ($font) {
                if ($DEBUGCSS) {
                    print "<pre>[get_font_family:";
                    print '(' . $this->_props_computed["font_family"] . '.' . $font_style . '.' . $weight . '.' . $subtype . ')';
                    print '(' . $font . ")get_font_family]\n</pre>";
                }
                return $font;
            }
        }

        $family = null;
        if ($DEBUGCSS) {
            print '(default)';
        }
        $font = $this->getFontMetrics()->getFont($family, $subtype);

        if ($font) {
            if ($DEBUGCSS) {
                print '(' . $font . ")get_font_family]\n</pre>";
            }
            return $font;
        }

        throw new Exception("Unable to find a suitable font replacement for: '" . $this->_props_computed["font_family"] . "'");
    }

    /**
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
     * @return float
     */
    function get_word_spacing()
    {
        $word_spacing = $this->_props_computed["word_spacing"];

        if ($word_spacing === "normal") {
            return 0;
        }

        if (strpos($word_spacing, "%") !== false) {
            return $word_spacing;
        }

        return (float)$this->length_in_pt($word_spacing, $this->__get("font_size"));
    }

    /**
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
     * @return float
     */
    function get_letter_spacing()
    {
        $letter_spacing = $this->_props_computed["letter_spacing"];

        if ($letter_spacing === "normal") {
            return 0;
        }

        return (float)$this->length_in_pt($letter_spacing, $this->__get("font_size"));
    }

    /**
     * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
     * @return float
     */
    function get_line_height()
    {
        $line_height = $this->_props_computed["line_height"];

        if ($line_height === "normal") {
            return self::$default_line_height * $this->__get("font_size");
        }

        if (is_numeric($line_height)) {
            return $line_height * $this->__get("font_size");
        }

        return (float)$this->length_in_pt($line_height, $this->__get("font_size"));
    }

    /**
     * @param string $prop
     * @param bool $current_is_parent
     * @return array|string
     */
    protected function get_prop_color(string $prop, bool $current_is_parent = false)
    {
        $val = $this->_props_computed[$prop];

        if ($val === "currentcolor") {
            // https://www.w3.org/TR/css-color-4/#resolving-other-colors
            if ($current_is_parent) {
                // Use the `color` value from the parent for the `color`
                // property itself
                return isset($this->parent_style)
                    ? $this->parent_style->__get("color")
                    : $this->munge_color(self::$_defaults[$prop]);
            }

            return $this->__get("color");
        }

        return $this->munge_color($val) ?? "transparent";
    }

    /**
     * Returns the color as an array
     *
     * The array has the following format:
     * <code>array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")</code>
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
     * @return array
     */
    function get_color()
    {
        return $this->get_prop_color("color", true);
    }

    /**
     * Returns the background color as an array
     *
     * The returned array has the same format as {@link Style::get_color()}
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
     * @return array
     */
    function get_background_color()
    {
        return $this->get_prop_color("background_color");
    }

    /**
     * Returns the background image URI, or "none"
     *
     * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
     * @return string
     */
    function get_background_image()
    {
        return $this->_stylesheet->resolve_url($this->_props_computed["background_image"]);
    }

    /**
     * Returns the background position as an array
     *
     * The returned array has the following format:
     * <code>array(x,y, "x" => x, "y" => y)</code>
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
     * @return array
     */
    function get_background_position()
    {
        $tmp = explode(" ", $this->_props_computed["background_position"]);

        return [
            0 => $tmp[0], "x" => $tmp[0],
            1 => $tmp[1], "y" => $tmp[1],
        ];
    }


    /**
     * Returns the background size as an array
     *
     * The return value has one of the following formats:
     * <code>"cover"</code>
     * <code>"contain"</code>
     * <code>array(width,height)</code>
     *
     * @link https://www.w3.org/TR/css3-background/#background-size
     * @return string|array
     */
    function get_background_size()
    {
        switch ($this->_props_computed["background_size"]) {
            case "cover":
                return "cover";
            case "contain":
                return "contain";
            default:
                break;
        }

        $result = explode(" ", $this->_props_computed["background_size"]);
        return [$result[0], $result[1]];
    }

    /**#@+
     * Returns the border color as an array
     *
     * See {@link Style::get_color()}
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
     * @return array
     */
    function get_border_top_color()
    {
        return $this->get_prop_color("border_top_color");
    }

    /**
     * @return array
     */
    function get_border_right_color()
    {
        return $this->get_prop_color("border_right_color");
    }

    /**
     * @return array
     */
    function get_border_bottom_color()
    {
        return $this->get_prop_color("border_bottom_color");
    }

    /**
     * @return array
     */
    function get_border_left_color()
    {
        return $this->get_prop_color("border_left_color");
    }

    /**#@-*/

    /**
     * Return an array of all border properties.
     *
     * The returned array has the following structure:
     * <code>
     * array("top" => array("width" => [border-width],
     *                      "style" => [border-style],
     *                      "color" => [border-color (array)]),
     *       "bottom" ... )
     * </code>
     *
     * @return array
     */
    function get_border_properties()
    {
        return [
            "top" => [
                "width" => $this->__get("border_top_width"),
                "style" => $this->__get("border_top_style"),
                "color" => $this->__get("border_top_color"),
            ],
            "bottom" => [
                "width" => $this->__get("border_bottom_width"),
                "style" => $this->__get("border_bottom_style"),
                "color" => $this->__get("border_bottom_color"),
            ],
            "right" => [
                "width" => $this->__get("border_right_width"),
                "style" => $this->__get("border_right_style"),
                "color" => $this->__get("border_right_color"),
            ],
            "left" => [
                "width" => $this->__get("border_left_width"),
                "style" => $this->__get("border_left_style"),
                "color" => $this->__get("border_left_color"),
            ],
        ];
    }

    /**
     * Return a single border property
     *
     * @param string $side
     *
     * @return mixed
     */
    protected function _get_border($side)
    {
        $color = $this->__get("border_" . $side . "_color");

        return $this->__get("border_" . $side . "_width") . " " .
            $this->__get("border_" . $side . "_style") . " " .
            (is_array($color) ? $color["hex"] : $color);
    }

    /**#@+
     * Return full border properties as a string
     *
     * Border properties are returned just as specified in CSS:
     * <pre>[width] [style] [color]</pre>
     * e.g. "1px solid blue"
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
     * @return string
     */
    function get_border_top()
    {
        return $this->_get_border("top");
    }

    /**
     * @return mixed
     */
    function get_border_right()
    {
        return $this->_get_border("right");
    }

    /**
     * @return mixed
     */
    function get_border_bottom()
    {
        return $this->_get_border("bottom");
    }

    /**
     * @return mixed
     */
    function get_border_left()
    {
        return $this->_get_border("left");
    }

    /**
     * @deprecated
     * @param float $w
     * @param float $h
     * @return float[]
     */
    function get_computed_border_radius($w, $h)
    {
        return $this->resolve_border_radius([0, 0, $w, $h]);
    }

    public function has_border_radius(): bool
    {
        if (isset($this->has_border_radius_cache)) {
            return $this->has_border_radius_cache;
        }

        // Use a fixed ref size here. We don't know the border-box width here
        // and font size might be 0. Since we are only interested in whether
        // there is any border radius at all, this should do
        $tl = (float) $this->length_in_pt($this->border_top_left_radius, 10);
        $tr = (float) $this->length_in_pt($this->border_top_right_radius, 10);
        $br = (float) $this->length_in_pt($this->border_bottom_right_radius, 10);
        $bl = (float) $this->length_in_pt($this->border_bottom_left_radius, 10);

        $this->has_border_radius_cache = $tl + $tr + $br + $bl > 0;
        return $this->has_border_radius_cache;
    }

    /**
     * Get the final border-radius values to use.
     *
     * Percentage values are resolved relative to the width of the border box.
     * The border radius is additionally scaled for the given render box, and
     * constrained by its width and height.
     *
     * @param float[]      $border_box The border box of the frame.
     * @param float[]|null $render_box The box to resolve the border radius for.
     *
     * @return float[] A 4-tuple of top-left, top-right, bottom-right, and bottom-left radius.
     */
    public function resolve_border_radius(
        array $border_box,
        ?array $render_box = null
    ): array {
        $render_box = $render_box ?? $border_box;
        $use_cache = $render_box === $border_box;

        if ($use_cache && isset($this->resolved_border_radius)) {
            return $this->resolved_border_radius;
        }

        [$x, $y, $w, $h] = $border_box;

        // Resolve percentages relative to width, as long as we have no support
        // for per-axis radii
        $tl = (float) $this->length_in_pt($this->border_top_left_radius, $w);
        $tr = (float) $this->length_in_pt($this->border_top_right_radius, $w);
        $br = (float) $this->length_in_pt($this->border_bottom_right_radius, $w);
        $bl = (float) $this->length_in_pt($this->border_bottom_left_radius, $w);

        if ($tl + $tr + $br + $bl > 0) {
            [$rx, $ry, $rw, $rh] = $render_box;

            $t_offset = $y - $ry;
            $r_offset = $rx + $rw - $x - $w;
            $b_offset = $ry + $rh - $y - $h;
            $l_offset = $x - $rx;

            if ($tl > 0) {
                $tl = max($tl + ($t_offset + $l_offset) / 2, 0);
            }
            if ($tr > 0) {
                $tr = max($tr + ($t_offset + $r_offset) / 2, 0);
            }
            if ($br > 0) {
                $br = max($br + ($b_offset + $r_offset) / 2, 0);
            }
            if ($bl > 0) {
                $bl = max($bl + ($b_offset + $l_offset) / 2, 0);
            }

            if ($tl + $bl > $rh) {
                $f = $rh / ($tl + $bl);
                $tl = $f * $tl;
                $bl = $f * $bl;
            }
            if ($tr + $br > $rh) {
                $f = $rh / ($tr + $br);
                $tr = $f * $tr;
                $br = $f * $br;
            }
            if ($tl + $tr > $rw) {
                $f = $rw / ($tl + $tr);
                $tl = $f * $tl;
                $tr = $f * $tr;
            }
            if ($bl + $br > $rw) {
                $f = $rw / ($bl + $br);
                $bl = $f * $bl;
                $br = $f * $br;
            }
        }

        $values = [$tl, $tr, $br, $bl];

        if ($use_cache) {
            $this->resolved_border_radius = $values;
        }

        return $values;
    }

    /**
     * Returns the outline color as an array
     *
     * See {@link Style::get_color()}
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
     * @return array
     */
    function get_outline_color()
    {
        return $this->get_prop_color("outline_color");
    }

    /**
     * Return full outline properties as a string
     *
     * Outline properties are returned just as specified in CSS:
     * <pre>[width] [style] [color]</pre>
     * e.g. "1px solid blue"
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
     * @return string
     */
    function get_outline()
    {
        $color = $this->__get("outline_color");

        return $this->__get("outline_width") . " " .
            $this->__get("outline_style") . " " .
            (is_array($color) ? $color["hex"] : $color);
    }

    /**
     * Returns border spacing as an array
     *
     * The array has the format (h_space,v_space)
     *
     * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
     * @return array
     */
    function get_border_spacing()
    {
        $arr = explode(" ", $this->_props_computed["border_spacing"]);
        if (count($arr) == 1) {
            $arr[1] = $arr[0];
        }
        return $arr;
    }

    /**
     * Returns the list style image URI, or "none"
     *
     * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
     * @return string
     */
    function get_list_style_image()
    {
        return $this->_stylesheet->resolve_url($this->_props_computed["list_style_image"]);
    }

    /**
     * @param string $value
     * @param int    $default
     *
     * @return array|string
     */
    protected function parse_counter_prop(string $value, int $default)
    {
        $ident = self::CSS_IDENTIFIER;
        $integer = self::CSS_INTEGER;
        $pattern = "/($ident)(?:\s+($integer))?/";

        if (!preg_match_all($pattern, $value, $matches, PREG_SET_ORDER)) {
            return "none";
        }

        $counters = [];

        foreach ($matches as $match) {
            $counter = $match[1];
            $value = isset($match[2]) ? (int) $match[2] : $default;
            $counters[$counter] = $value;
        }

        return $counters;
    }

    /**
     * @return array|string
     */
    function get_counter_increment()
    {
        $val = $this->_props_computed["counter_increment"];

        if ($val === "none" || $val === "inherit") {
            return "none";
        }

        return $this->parse_counter_prop($val, 1);
    }

    /**
     * @return array|string
     */
    protected function get_counter_reset()
    {
        $val = $this->_props_computed["counter_reset"];

        if ($val === "none") {
            return "none";
        }

        return $this->parse_counter_prop($val, 0);
    }

    /**
     * @return string[]|string
     */
    protected function get_content()
    {
        $val = $this->_props_computed["content"];

        if ($val === "normal" || $val === "none") {
            return $val;
        }

        return $this->parse_property_value($val);
    }

    /*==============================*/

    /**
     * Parse a property value into its components.
     *
     * @param string $value
     *
     * @return string[]
     */
    protected function parse_property_value(string $value): array
    {
        $ident = self::CSS_IDENTIFIER;
        $number = self::CSS_NUMBER;

        $pattern = "/\n" .
            "\s* \" ( (?:[^\"]|\\\\[\"])* ) (?<!\\\\)\" |\n" . // String ""
            "\s* '  ( (?:[^']|\\\\['])* )   (?<!\\\\)'  |\n" . // String ''
            "\s* ($ident \\([^)]*\\) )                  |\n" . // Functional
            "\s* ($ident)                               |\n" . // Keyword
            "\s* (\#[0-9a-fA-F]*)                       |\n" . // Hex value
            "\s* ($number [a-zA-Z%]*)                   |\n" . // Number (+ unit/percentage)
            "\s* ([\/,;])                                \n" . // Delimiter
            "/Sx";

        if (!preg_match_all($pattern, $value, $matches)) {
            return [];
        }

        return array_map("trim", $matches[0]);
    }

    protected function is_color_value(string $val): bool
    {
        return $val === "currentcolor"
            || $val === "transparent"
            || isset(Color::$cssColorNames[$val])
            || preg_match("/^#|rgb\(|rgba\(|cmyk\(/", $val);
    }

    protected function prop_name(string $style, string $side, string $type): string
    {
        $prop = $style;
        if ($side !== "") {
            $prop .= "_" . $side;
        };
        if ($type !== "") {
            $prop .= "_" . $type;
        };
        return $prop;
    }

    /**
     * Generalized set function for individual attribute of combined style.
     *
     * Applicable for margin, border, padding, outline.
     *
     * @param string $style
     * @param string $side
     * @param string $type
     * @param mixed $val
     */
    protected function _set_style_side_type($style, $side, $type, $val)
    {
        $prop = $this->prop_name($style, $side, $type);

        $this->_prop_cache[$prop] = null;

        if ($val === "inherit") {
            $this->_props_computed[$prop] = null;
            return;
        }

        if ($side === "bottom") {
            $this->_computed_bottom_spacing = null; //reset computed cache, border style can disable/enable border calculations
        }

        if ($type === "color") {
            $this->set_prop_color($prop, $val);
        } elseif (($style === "border" || $style === "outline") && $type === "width") {
            // Border-width keywords
            if ($val === "thin") {
                $val_computed = 0.5;
            } elseif ($val === "medium") {
                $val_computed = 1.5;
            } elseif ($val === "thick") {
                $val_computed = 2.5;
            } elseif (mb_strpos($val, "%") !== false) {
                $val_computed = null;
            } else {
                $val_computed = $this->single_length_in_pt($val);

                if ($val_computed < 0) {
                    $val_computed = null;
                }
            }

            if ($val_computed === null) {
                $this->_props_computed[$prop] = null;
            } else {
                $line_style_prop = $this->prop_name($style, $side, "style");
                $line_style = $this->__get($line_style_prop);
                $has_line_style = $line_style !== "none" && $line_style !== "hidden";

                $this->_props_computed[$prop] = $has_line_style ? $val_computed : 0;
            }
        } elseif (($style === "border" || $style === "outline") && $type === "style") {
            if (in_array($val, Style::$BORDER_STYLES, true)) {
                $this->_props_computed[$prop] = $val;
            } else {
                $this->_props_computed[$prop] = null;
            }
        } elseif ($style === "margin" || $style === "padding") {
            if ($val === "none") {
                // Legacy support for `none` keyword, not covered by spec
                $val_computed = 0;
            } elseif ($style === "margin" && $val === "auto") {
                $val_computed = $val;
            } elseif (mb_strpos($val, "%") !== false) {
                $val_computed = $val;
            } else {
                $val_computed = $this->single_length_in_pt($val);

                if ($style === "padding" && $val_computed < 0) {
                    $val_computed = null;
                }
            }

            $this->_props_computed[$prop] = $val_computed;
        } elseif ($val !== "") {
            $this->_props_computed[$prop] = $val;
        } else {
            $this->_props_computed[$prop] = null;
        }
    }

    /**
     * @param string $style
     * @param string $type
     * @param mixed $val
     * @param bool $important
     */
    protected function _set_style_type($style, $type, $val, $important)
    {
        $v = $this->parse_property_value($val);

        switch (count($v)) {
            case 1:
                [$top, $right, $bottom, $left] = [$v[0], $v[0], $v[0], $v[0]];
                break;
            case 2:
                [$top, $right, $bottom, $left] = [$v[0], $v[1], $v[0], $v[1]];
                break;
            case 3:
                [$top, $right, $bottom, $left] = [$v[0], $v[1], $v[2], $v[1]];
                break;
            case 4:
                [$top, $right, $bottom, $left] = [$v[0], $v[1], $v[2], $v[3]];
                break;
            default:
                return;
        }

        $this->set_prop($this->prop_name($style, "top", $type), $top, $important);
        $this->set_prop($this->prop_name($style, "right", $type), $right, $important);
        $this->set_prop($this->prop_name($style, "bottom", $type), $bottom, $important);
        $this->set_prop($this->prop_name($style, "left", $type), $left, $important);
    }

    /*======================*/

    /**
     * https://www.w3.org/TR/CSS21/visuren.html#display-prop
     *
     * @param string $val
     */
    protected function set_display(string $val): void
    {
        // Make sure that common valid, but unsupported display types have an
        // appropriate fallback display type
        switch ($val) {
            case "flow-root":
            case "flex":
            case "grid":
            case "table-caption":
                $val = "block";
                break;
            case "inline-flex":
            case "inline-grid":
                $val = "inline-block";
                break;
        }

        if (!isset(self::$valid_display_types[$val])) {
            return;
        }

        // https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
        if ($this->is_in_flow()) {
            $computed = $val;
        } else {
            switch ($val) {
                case "inline":
                case "inline-block":
                // case "table-row-group":
                // case "table-header-group":
                // case "table-footer-group":
                // case "table-row":
                // case "table-cell":
                // case "table-column-group":
                // case "table-column":
                // case "table-caption":
                    $computed = "block";
                    break;
                case "inline-table":
                    $computed = "table";
                    break;
                default:
                    $computed = $val;
                    break;
            }
        }

        $this->_props_computed["display"] = $computed;
    }

    protected function set_prop_color($prop, $val)
    {
        $this->_prop_cache[$prop] = null;

        // https://www.w3.org/TR/css-color-4/#resolving-other-colors
        $munged_color = $val !== "currentcolor"
            ? $this->munge_color($val)
            : $val;

        if (is_null($munged_color)) {
            $this->_props_computed[$prop] = null;
            return;
        }

        $this->_props_computed[$prop] = is_array($munged_color) ? $munged_color["hex"] : $munged_color;
    }

    /**
     * Sets color
     *
     * The color parameter can be any valid CSS color value
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
     * @param string $color
     */
    function set_color($color)
    {
        $this->set_prop_color("color", $color);
    }

    /**
     * Sets the background color
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
     * @param string $color
     */
    function set_background_color($color)
    {
        $this->set_prop_color("background_color", $color);
    }

    /**
     * Set the background image url
     * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image
     *
     * @param string $val
     */
    function set_background_image($val)
    {
        $this->_prop_cache["background_image"] = null;

        $parsed_val = $this->_stylesheet->resolve_url($val);

        if ($parsed_val === "none") {
            $this->_props_computed["background_image"] = "none";
        } else {
            $this->_props_computed["background_image"] = "url(" . $parsed_val . ")";
        }
    }

    /**
     * Sets the background repeat
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
     * @param string $val
     */
    function set_background_repeat($val)
    {
        $this->_prop_cache["background_repeat"] = null;

        if ($val === "inherit") {
            $this->_props_computed["background_repeat"] = null;
            return;
        }

        $this->_props_computed["background_repeat"] = $val;
    }

    /**
     * Sets the background attachment
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
     * @param string $val
     */
    function set_background_attachment($val)
    {
        $this->_prop_cache["background_attachment"] = null;

        if ($val === "inherit") {
            $this->_props_computed["background_attachment"] = null;
            return;
        }

        $this->_props_computed["background_attachment"] = $val;
    }

    /**
     * Sets the background position
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
     * @param string $val
     */
    function set_background_position($val)
    {
        $this->_prop_cache["background_position"] = null;

        $tmp = explode(" ", $val);

        switch ($tmp[0]) {
            case "left":
                $x = "0%";
                break;

            case "right":
                $x = "100%";
                break;

            case "top":
                $y = "0%";
                break;

            case "bottom":
                $y = "100%";
                break;

            case "center":
                $x = "50%";
                $y = "50%";
                break;

            default:
                $x = $tmp[0];
                break;
        }

        if (isset($tmp[1])) {
            switch ($tmp[1]) {
                case "left":
                    $x = "0%";
                    break;

                case "right":
                    $x = "100%";
                    break;

                case "top":
                    $y = "0%";
                    break;

                case "bottom":
                    $y = "100%";
                    break;

                case "center":
                    if ($tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center") {
                        $y = "50%";
                    } else {
                        $x = "50%";
                    }
                    break;

                default:
                    $y = $tmp[1];
                    break;
            }
        } else {
            $y = "50%";
        }

        if (!isset($x)) {
            $x = "0%";
        }

        if (!isset($y)) {
            $y = "0%";
        }
        
        $this->_props_computed["background_position"] = "$x $y";
    }

    /**
     * Sets the background size
     *
     * @link https://www.w3.org/TR/css3-background/#background-size
     * @param string $val
     */
    function set_background_size($val)
    {
        $this->_prop_cache["background_size"] = null;

        $result = explode(" ", $val);
        $width = $result[0];

        switch ($width) {
            case "cover":
            case "contain":
                $this->_props_computed["background_size"] = $width;
                return;
            case "inherit":
                $this->_props_computed["background_size"] = null;
                return;
        }

        if ($width !== "auto" && strpos($width, "%") === false) {
            $width = (float)$this->length_in_pt($width);
        }

        $height = $result[1] ?? "auto";
        if ($height !== "auto" && strpos($height, "%") === false) {
            $height = (float)$this->length_in_pt($height);
        }

        $this->_props_computed["background_size"] = "$width $height";
    }

    /**
     * Sets the background - combined options
     *
     * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
     * @param string $value
     * @param bool $important
     */
    function set_background($value, bool $important = false)
    {
        if ($value === "none") {
            $this->set_prop("background_image", "none", $important);
            $this->set_prop("background_color", "transparent", $important);
        } else {
            $components = $this->parse_property_value($value);
            $pos_size = [];

            foreach ($components as $val) {
                if ($val === "none" || mb_substr($val, 0, 4) === "url(") {
                    $this->set_prop("background_image", $val, $important);
                } elseif ($val === "fixed" || $val === "scroll") {
                    $this->set_prop("background_attachment", $val, $important);
                } elseif ($val === "repeat" || $val === "repeat-x" || $val === "repeat-y" || $val === "no-repeat") {
                    $this->set_prop("background_repeat", $val, $important);
                } elseif ($this->is_color_value($val)) {
                    $this->set_prop("background_color", $val, $important);
                } else {
                    $pos_size[] = $val;
                }
            }

            if (count($pos_size)) {
                // Split value list at "/"
                $index = array_search("/", $pos_size, true);

                if ($index !== false) {
                    $pos = array_slice($pos_size, 0, $index);
                    $size = array_slice($pos_size, $index + 1);
                } else {
                    $pos = $pos_size;
                    $size = [];
                }

                $this->set_prop("background_position", implode(" ", $pos), $important);

                if (count($size)) {
                    $this->set_prop("background_size", implode(" ", $size), $important);
                }
            }
        }
    }

    /**
     * Sets the font size
     *
     * $size can be any acceptable CSS size
     *
     * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
     * @param string|float $size
     */
    function set_font_size($size)
    {
        $this->_prop_cache["font_size"] = null;

        if ($size === "inherit") {
            $this->_props_computed["font_size"] = null;
            return;
        }

        $parent_font_size = isset($this->parent_style)
            ? $this->parent_style->__get("font_size")
            : self::$default_font_size;

        switch ((string)$size) {
            case "xx-small":
            case "x-small":
            case "small":
            case "medium":
            case "large":
            case "x-large":
            case "xx-large":
                $fs = self::$default_font_size * self::$font_size_keywords[$size];
                break;

            case "smaller":
                $fs = 8 / 9 * $parent_font_size;
                break;

            case "larger":
                $fs = 6 / 5 * $parent_font_size;
                break;

            default:
                $fs = $this->single_length_in_pt($size, $parent_font_size, $parent_font_size);
                break;
        }

        $this->_props_computed["font_size"] = $fs;
    }

    /**
     * Sets the font weight
     *
     * @param string|int $weight
     */
    function set_font_weight($weight)
    {
        $this->_prop_cache["font_weight"] = null;

        if ($weight === "inherit") {
            $this->_props_computed["font_weight"] = null;
            return;
        }

        $computed_weight = $weight;

        if ($weight === "bolder") {
            //TODO: One font weight heavier than the parent element (among the available weights of the font).
            $computed_weight = "bold";
        } elseif ($weight === "lighter") {
            //TODO: One font weight lighter than the parent element (among the available weights of the font).
            $computed_weight = "normal";
        }

        $this->_props_computed["font_weight"] = $computed_weight;
    }

    /**
     * Sets the font style
     *
     * combined attributes
     * set individual attributes also, respecting !important mark
     * exactly this order, separate by space. Multiple fonts separated by comma:
     * font-style, font-variant, font-weight, font-size, line-height, font-family
     *
     * Other than with border and list, existing partial attributes should
     * reset when starting here, even when not mentioned.
     * If individual attribute is !important and explicit or implicit replacement is not,
     * keep individual attribute
     *
     * require whitespace as delimiters for single value attributes
     * On delimiter "/" treat first as font height, second as line height
     * treat all remaining at the end of line as font
     * font-style, font-variant, font-weight, font-size, line-height, font-family
     *
     * missing font-size and font-family might be not allowed, but accept it here and
     * use default (medium size, empty font name)
     *
     * @link https://www.w3.org/TR/CSS21/fonts.html#font-shorthand
     * @param string $val
     * @param bool $important
     */
    function set_font($val, bool $important = false)
    {
        if (preg_match("/^(italic|oblique|normal)\s*(.*)$/i", $val, $match)) {
            $this->set_prop("font_style", $match[1], $important);
            $val = $match[2];
        }

        if (preg_match("/^(small-caps|normal)\s*(.*)$/i", $val, $match)) {
            $this->set_prop("font_variant", $match[1], $important);
            $val = $match[2];
        }

        //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip!
        if (preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) &&
            !preg_match("/^(?:pt|px|pc|rem|em|ex|in|cm|mm|%)/", $match[2])
        ) {
            $this->set_prop("font_weight", $match[1], $important);
            $val = $match[2];
        }

        if (preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%))(?:\/|\s*)(.*)$/i", $val, $match)) {
            $this->set_prop("font_size", $match[1], $important);
            $val = $match[2];
            if (preg_match("/^(?:\/|\s*)(\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%)?)\s*(.*)$/i", $val, $match)) {
                $this->set_prop("line_height", $match[1], $important);
                $val = $match[2];
            }
        }

        if (strlen($val) != 0) {
            $this->set_prop("font_family", $val, $important);
        }
    }

    /**
     * Sets the text alignment
     *
     * If no alignment is set on the element and the direction is rtl then
     * the property is set to "right", otherwise it is set to "left".
     *
     * @link https://www.w3.org/TR/CSS21/text.html#propdef-text-align
     */
    public function set_text_align($val)
    {
        $this->_prop_cache["text_align"] = null;

        $alignment = $val;
        if ($alignment === "") {
            $alignment = "left";
            if ($this->__get("direction") === "rtl") {
                $alignment = "right";
            }
        }

        if (!in_array($alignment, self::$text_align_keywords, true)) {
            $this->_props_computed["text_align"] = null;
            return;
        }

        $this->_props_computed["text_align"] = $alignment;
    }

    /**
     * Sets word spacing property
     *
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
     * @param $val
     */
    function set_word_spacing($val)
    {
        $this->_prop_cache["word_spacing"] = null;

        if ($val === "inherit") {
            $this->_props_computed["word_spacing"] = null;
            return;
        }

        if ($val === "normal" || strpos($val, "%") !== false) {
            $this->_props_computed["word_spacing"] = $val;
        } else {
            $this->_props_computed["word_spacing"] = ((float)$this->length_in_pt($val, $this->__get("font_size"))) . "pt";
        }
    }

    /**
     * Sets letter spacing property
     *
     * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
     * @param $val
     */
    function set_letter_spacing($val)
    {
        $this->_prop_cache["letter_spacing"] = null;

        if ($val === "inherit") {
            $this->_props_computed["letter_spacing"] = null;
            return;
        }

        if ($val === "normal") {
            $this->_props_computed["letter_spacing"] = $val;
        } else {
            $this->_props_computed["letter_spacing"] = ((float)$this->length_in_pt($val, $this->__get("font_size"))) . "pt";
        }
    }

    /**
     * Sets line height property
     *
     * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
     * @param $val
     */
    function set_line_height($val)
    {
        $this->_prop_cache["line_height"] = null;

        if ($val === "inherit") {
            $this->_props_computed["line_height"] = null;
            return;
        }

        if ($val === "normal" || is_numeric($val)) {
            $this->_props_computed["line_height"] = $val;
        } else {
            $this->_props_computed["line_height"] = ((float)$this->length_in_pt($val, $this->__get("font_size"))) . "pt";
        }
    }

    /**
     * Sets page break properties
     *
     * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
     * @param string $break
     */
    function set_page_break_before($break)
    {
        $this->_prop_cache["page_break_before"] = null;

        if ($break === "inherit") {
            $this->_props_computed["page_break_before"] = null;
            return;
        }

        if ($break === "left" || $break === "right") {
            $break = "always";
        }

        $this->_props_computed["page_break_before"] = $break;
    }

    /**
     * @param $break
     */
    function set_page_break_after($break)
    {
        $this->_prop_cache["page_break_after"] = null;

        if ($break === "inherit") {
            $this->_props_computed["page_break_after"] = null;
            return;
        }

        if ($break === "left" || $break === "right") {
            $break = "always";
        }

        $this->_props_computed["page_break_after"] = $break;
    }

    /**
     * Sets the margin size
     *
     * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
     * @param $val
     */
    function set_margin_top($val)
    {
        $this->_set_style_side_type("margin", "top", "", $val);
    }

    /**
     * @param $val
     */
    function set_margin_right($val)
    {
        $this->_set_style_side_type("margin", "right", "", $val);
    }

    /**
     * @param $val
     */
    function set_margin_bottom($val)
    {
        $this->_set_style_side_type("margin", "bottom", "", $val);
    }

    /**
     * @param $val
     */
    function set_margin_left($val)
    {
        $this->_set_style_side_type("margin", "left", "", $val);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_margin($val, bool $important = false)
    {
        $this->_set_style_type("margin", "", $val, $important);
    }

    /**
     * Sets the padding size
     *
     * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
     * @param $val
     */
    function set_padding_top($val)
    {
        $this->_set_style_side_type("padding", "top", "", $val);
    }

    /**
     * @param $val
     */
    function set_padding_right($val)
    {
        $this->_set_style_side_type("padding", "right", "", $val);
    }

    /**
     * @param $val
     */
    function set_padding_bottom($val)
    {
        $this->_set_style_side_type("padding", "bottom", "", $val);
    }

    /**
     * @param $val
     */
    function set_padding_left($val)
    {
        $this->_set_style_side_type("padding", "left", "", $val);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_padding($val, bool $important = false)
    {
        $this->_set_style_type("padding", "", $val, $important);
    }

    /**
     * Sets a single border
     *
     * @param string $side
     * @param string $border_spec ([width] [style] [color])
     * @param bool $important
     */
    protected function _set_border($side, $border_spec, bool $important)
    {
        $components = $this->parse_property_value($border_spec);

        foreach ($components as $val) {
            if (in_array($val, self::$BORDER_STYLES, true)) {
                $this->set_prop("border_${side}_style", $val, $important);
            } elseif ($this->is_color_value($val)) {
                $this->set_prop("border_${side}_color", $val, $important);
            } else {
                // Assume width
                $this->set_prop("border_${side}_width", $val, $important);
            }
        }
    }

    /**
     * @link http://www.w3.org/TR/CSS21/box.html#border-properties
     * @param string $val
     * @param bool $important
     */
    function set_border_top($val, bool $important = false)
    {
        $this->_set_border("top", $val, $important);
    }

    function set_border_top_color($val)
    {
        $this->_set_style_side_type("border", "top", "color", $val);
    }

    function set_border_top_style($val)
    {
        $this->_set_style_side_type("border", "top", "style", $val);
    }

    function set_border_top_width($val)
    {
        $this->_set_style_side_type("border", "top", "width", $val);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_right($val, bool $important = false)
    {
        $this->_set_border("right", $val, $important);
    }

    function set_border_right_color($val)
    {
        $this->_set_style_side_type("border", "right", "color", $val);
    }

    function set_border_right_style($val)
    {
        $this->_set_style_side_type("border", "right", "style", $val);
    }

    function set_border_right_width($val)
    {
        $this->_set_style_side_type("border", "right", "width", $val);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_bottom($val, bool $important = false)
    {
        $this->_set_border("bottom", $val, $important);
    }

    function set_border_bottom_color($val)
    {
        $this->_set_style_side_type("border", "bottom", "color", $val);
    }

    function set_border_bottom_style($val)
    {
        $this->_set_style_side_type("border", "bottom", "style", $val);
    }

    function set_border_bottom_width($val)
    {
        $this->_set_style_side_type("border", "bottom", "width", $val);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_left($val, bool $important = false)
    {
        $this->_set_border("left", $val, $important);
    }

    function set_border_left_color($val)
    {
        $this->_set_style_side_type("border", "left", "color", $val);
    }

    function set_border_left_style($val)
    {
        $this->_set_style_side_type("border", "left", "style", $val);
    }

    function set_border_left_width($val)
    {
        $this->_set_style_side_type("border", "left", "width", $val);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border($val, bool $important = false)
    {
        $this->_set_border("top", $val, $important);
        $this->_set_border("right", $val, $important);
        $this->_set_border("bottom", $val, $important);
        $this->_set_border("left", $val, $important);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_width($val, bool $important = false)
    {
        $this->_set_style_type("border", "width", $val, $important);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_color($val, bool $important = false)
    {
        $this->_set_style_type("border", "color", $val, $important);
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_style($val, bool $important = false)
    {
        $this->_set_style_type("border", "style", $val, $important);
    }

    /**
     * Sets the border radius size
     *
     * http://www.w3.org/TR/css3-background/#corners
     *
     * @param string $val
     */
    function set_border_top_left_radius($val)
    {
        $this->_set_border_radius_corner($val, "top_left");
    }

    /**
     * @param string $val
     */
    function set_border_top_right_radius($val)
    {
        $this->_set_border_radius_corner($val, "top_right");
    }

    /**
     * @param string $val
     */
    function set_border_bottom_left_radius($val)
    {
        $this->_set_border_radius_corner($val, "bottom_left");
    }

    /**
     * @param string $val
     */
    function set_border_bottom_right_radius($val)
    {
        $this->_set_border_radius_corner($val, "bottom_right");
    }

    /**
     * @param string $val
     * @param bool $important
     */
    function set_border_radius($val, bool $important = false)
    {
        $r = $this->parse_property_value($val);

        switch (count($r)) {
            case 1:
                [$tl, $tr, $br, $bl] = [$r[0], $r[0], $r[0], $r[0]];
                break;
            case 2:
                [$tl, $tr, $br, $bl] = [$r[0], $r[1], $r[0], $r[1]];
                break;
            case 3:
                [$tl, $tr, $br, $bl] = [$r[0], $r[1], $r[2], $r[1]];
                break;
            case 4:
                [$tl, $tr, $br, $bl] = [$r[0], $r[1], $r[2], $r[3]];
                break;
            default:
                return;
        }

        $this->set_prop("border_top_left_radius", $tl, $important);
        $this->set_prop("border_top_right_radius", $tr, $important);
        $this->set_prop("border_bottom_right_radius", $br, $important);
        $this->set_prop("border_bottom_left_radius", $bl, $important);
    }

    /**
     * @param string $val
     * @param string $corner
     */
    protected function _set_border_radius_corner($val, $corner)
    {
        $prop = "border_" . $corner . "_radius";

        $this->_prop_cache[$prop] = null;

        if ($val === "inherit") {
            $this->_props_computed[$prop] = null;
            return;
        }

        $computed = mb_strpos($val, "%") === false
            ? $this->single_length_in_pt($val)
            : $val;

        $this->_props_computed[$prop] = $computed;
    }

    /**
     * @return float|int|string
     */
    function get_border_top_left_radius()
    {
        return $this->_get_border_radius_corner("top_left");
    }

    /**
     * @return float|int|string
     */
    function get_border_top_right_radius()
    {
        return $this->_get_border_radius_corner("top_right");
    }

    /**
     * @return float|int|string
     */
    function get_border_bottom_left_radius()
    {
        return $this->_get_border_radius_corner("bottom_left");
    }

    /**
     * @return float|int|string
     */
    function get_border_bottom_right_radius()
    {
        return $this->_get_border_radius_corner("bottom_right");
    }

    /**
     * @param $corner
     * @return float|int|string
     */
    protected function _get_border_radius_corner($corner)
    {
        $prop = "border_" . $corner . "_radius";

        if (!isset($this->_props_computed[$prop])) {
            return 0;
        }

        return $this->_props_computed[$prop];
    }

    /**
     * Sets the outline styles
     *
     * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
     * @param string $value
     * @param bool $important
     */
    function set_outline($value, bool $important = false)
    {
        $components = $this->parse_property_value($value);

        foreach ($components as $val) {
            if (in_array($val, self::$BORDER_STYLES, true)) {
                $this->set_prop("outline_style", $val, $important);
            } elseif ($this->is_color_value($val)) {
                $this->set_prop("outline_color", $val, $important);
            } else {
                // Assume width
                $this->set_prop("outline_width", $val, $important);
            }
        }
    }

    /**
     * @param $val
     */
    function set_outline_width($val)
    {
        $this->_set_style_side_type("outline", "", "width", $val);
    }

    /**
     * @param $val
     */
    function set_outline_color($val)
    {
        $this->_set_style_side_type("outline", "", "color", $val);
    }

    /**
     * @param $val
     */
    function set_outline_style($val)
    {
        $this->_set_style_side_type("outline", "", "style", $val);
    }

    /**
     * Sets the border spacing
     *
     * @link http://www.w3.org/TR/CSS21/box.html#border-properties
     * @param float $val
     */
    function set_border_spacing($val)
    {
        $this->_prop_cache["border_spacing"] = null;

        if ($val === "inherit") {
            $this->_props_computed["border_spacing"] = null;
            return;
        }

        $arr = explode(" ", $val);

        if (count($arr) === 1) {
            $arr[1] = $arr[0];
        }

        $this->_props_computed["border_spacing"] = "$arr[0] $arr[1]";
    }

    /**
     * Sets the list style image
     *
     * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
     * @param $val
     */
    function set_list_style_image($val)
    {
        $this->_prop_cache["list_style_image"] = null;

        if ($val === "inherit") {
            $this->_props_computed["list_style_image"] = null;
            return;
        }

        $parsed_val = $this->_stylesheet->resolve_url($val);

        if ($parsed_val === "none") {
            $this->_props_computed["list_style_image"] = "none";
        } else {
            $this->_props_computed["list_style_image"] = "url(" . $parsed_val . ")";
        }
    }

    /**
     * Sets the list style
     *
     * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
     * @param string $value
     * @param bool $important
     */
    function set_list_style($value, bool $important = false)
    {
        static $positions = ["inside", "outside"];
        static $types = [
            "disc", "circle", "square",
            "decimal-leading-zero", "decimal", "1",
            "lower-roman", "upper-roman", "a", "A",
            "lower-greek",
            "lower-latin", "upper-latin",
            "lower-alpha", "upper-alpha",
            "armenian", "georgian", "hebrew",
            "cjk-ideographic", "hiragana", "katakana",
            "hiragana-iroha", "katakana-iroha", "none"
        ];

        $components = $this->parse_property_value($value);

        foreach ($components as $val) {
            /* http://www.w3.org/TR/CSS21/generate.html#list-style
             * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
             */
            if ($val === "none") {
                $this->set_prop("list_style_type", $val, $important);
                $this->set_prop("list_style_image", $val, $important);
                continue;
            }

            //On setting or merging or inheriting list_style_image as well as list_style_type,
            //and url exists, then url has precedence, otherwise fall back to list_style_type
            //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type)
            //Internet Explorer 7/8 and dompdf is right.

            if (mb_substr($val, 0, 4) === "url(") {
                $this->set_prop("list_style_image", $val, $important);
                continue;
            }

            if (in_array($val, $types, true)) {
                $this->set_prop("list_style_type", $val, $important);
            } elseif (in_array($val, $positions, true)) {
                $this->set_prop("list_style_position", $val, $important);
            }
        }
    }

    /**
     * @param $val
     */
    function set_size($val)
    {
        $this->_prop_cache["size"] = null;

        $length_re = "/(\d+\s*(?:pt|px|pc|rem|em|ex|in|cm|mm|%))/";

        $val = mb_strtolower($val);

        if ($val === "auto") {
            $this->_props_computed["size"] = $val;
            return;
        }

        $parts = preg_split("/\s+/", $val);

        $computed = [];
        if (preg_match($length_re, $parts[0])) {
            $computed[] = $this->length_in_pt($parts[0]);

            if (isset($parts[1]) && preg_match($length_re, $parts[1])) {
                $computed[] = $this->length_in_pt($parts[1]);
            } else {
                $computed[] = $computed[0];
            }

            if (isset($parts[2]) && $parts[2] === "landscape") {
                $computed = array_reverse($computed);
            }
        } elseif (isset(CPDF::$PAPER_SIZES[$parts[0]])) {
            $computed = array_slice(CPDF::$PAPER_SIZES[$parts[0]], 2, 2);

            if (isset($parts[1]) && $parts[1] === "landscape") {
                $computed = array_reverse($computed);
            }
        } else {
            $this->_props_computed["size"] = null;
            return;
        }

        $this->_props_computed["size"] = $computed;
    }

    /**
     * Gets the CSS3 transform property
     *
     * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property
     * @return array|null
     */
    function get_transform()
    {
        //TODO: should be handled in setter (lengths set to absolute)

        $number = "\s*([^,\s]+)\s*";
        $tr_value = "\s*([^,\s]+)\s*";
        $angle = "\s*([^,\s]+(?:deg|rad)?)\s*";

        if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $this->_props_computed["transform"], $parts, PREG_SET_ORDER)) {
            return null;
        }

        $functions = [
            //"matrix"     => "\($number,$number,$number,$number,$number,$number\)",

            "translate" => "\($tr_value(?:,$tr_value)?\)",
            "translateX" => "\($tr_value\)",
            "translateY" => "\($tr_value\)",

            "scale" => "\($number(?:,$number)?\)",
            "scaleX" => "\($number\)",
            "scaleY" => "\($number\)",

            "rotate" => "\($angle\)",

            "skew" => "\($angle(?:,$angle)?\)",
            "skewX" => "\($angle\)",
            "skewY" => "\($angle\)",
        ];

        $transforms = [];

        foreach ($parts as $part) {
            $t = $part[0];

            foreach ($functions as $name => $pattern) {
                if (preg_match("/$name\s*$pattern/i", $t, $matches)) {
                    $values = array_slice($matches, 1);

                    switch ($name) {
                        // <angle> units
                        case "rotate":
                        case "skew":
                        case "skewX":
                        case "skewY":

                            foreach ($values as $i => $value) {
                                if (strpos($value, "rad")) {
                                    $values[$i] = rad2deg(floatval($value));
                                } else {
                                    $values[$i] = floatval($value);
                                }
                            }

                            switch ($name) {
                                case "skew":
                                    if (!isset($values[1])) {
                                        $values[1] = 0;
                                    }
                                    break;
                                case "skewX":
                                    $name = "skew";
                                    $values = [$values[0], 0];
                                    break;
                                case "skewY":
                                    $name = "skew";
                                    $values = [0, $values[0]];
                                    break;
                            }
                            break;

                        // <translation-value> units
                        case "translate":
                            $values[0] = $this->length_in_pt($values[0], (float)$this->length_in_pt($this->width));

                            if (isset($values[1])) {
                                $values[1] = $this->length_in_pt($values[1], (float)$this->length_in_pt($this->height));
                            } else {
                                $values[1] = 0;
                            }
                            break;

                        case "translateX":
                            $name = "translate";
                            $values = [$this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)), 0];
                            break;

                        case "translateY":
                            $name = "translate";
                            $values = [0, $this->length_in_pt($values[0], (float)$this->length_in_pt($this->height))];
                            break;

                        // <number> units
                        case "scale":
                            if (!isset($values[1])) {
                                $values[1] = $values[0];
                            }
                            break;

                        case "scaleX":
                            $name = "scale";
                            $values = [$values[0], 1.0];
                            break;

                        case "scaleY":
                            $name = "scale";
                            $values = [1.0, $values[0]];
                            break;
                    }

                    $transforms[] = [
                        $name,
                        $values,
                    ];
                }
            }
        }

        return $transforms;
    }

    /**
     * @param $val
     */
    function set_transform($val)
    {
        $this->_prop_cache["transform"] = null;

        if ($val === "inherit") {
            $this->_props_computed["transform"] = null;
            return;
        }
        
        $this->_props_computed["transform"] = $val;
    }

    /**
     * Sets the CSS3 transform-origin property
     *
     * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
     * @param string $val
     */
    function set_transform_origin($val)
    {
        $this->_prop_cache["transform_origin"] = null;

        if ($val === "inherit") {
            $this->_props_computed["transform_origin"] = null;
            return;
        }

        $this->_props_computed["transform_origin"] = $val;
    }

    /**
     * Gets the CSS3 transform-origin property
     *
     * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
     * @return mixed[]
     */
    function get_transform_origin()
    {
        //TODO: should be handled in setter
        
        $values = preg_split("/\s+/", $this->_props_computed["transform_origin"]);

        $values = array_map(function ($value) {
            if (in_array($value, ["top", "left"])) {
                return 0;
            } else if (in_array($value, ["bottom", "right"])) {
                return "100%";
            } else {
                return $value;
            }
        }, $values);

        if (!isset($values[1])) {
            $values[1] = $values[0];
        }

        return $values;
    }

    /**
     * @param $val
     * @return null
     */
    protected function parse_image_resolution($val)
    {
        // If exif data could be get:
        // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';

        $re = '/^\s*(\d+|normal|auto)\s*$/';

        if (!preg_match($re, $val, $matches)) {
            return null;
        }

        return $matches[1];
    }

    /**
     * auto | normal | dpi
     *
     * @param $val
     */
    function set_background_image_resolution($val)
    {
        $this->_prop_cache["background_image_resolution"] = null;

        if ($val === "inherit") {
            $this->_props_computed["background_image_resolution"] = null;
            return;
        }

        $parsed = $this->parse_image_resolution($val);

        $this->_props_computed["background_image_resolution"] = $parsed;
    }

    /**
     * auto | normal | dpi
     *
     * @param $val
     */
    function set_image_resolution($val)
    {
        $this->_prop_cache["image_resolution"] = null;

        if ($val === "inherit") {
            $this->_props_computed["image_resolution"] = null;
            return;
        }

        $parsed = $this->parse_image_resolution($val);

        $this->_props_computed["image_resolution"] = $parsed;
    }

    /**
     * @param $val
     */
    function set_z_index($val)
    {
        $this->_prop_cache["z_index"] = null;

        if ($val === "inherit") {
            $this->_props_computed["z_index"] = null;
            return;
        }

        if ($val !== "auto" && round((float) $val) != $val) {
            $this->_props_computed["z_index"] = null;
            return;
        }

        $this->_props_computed["z_index"] = $val;
    }

    /**
     * @param FontMetrics $fontMetrics
     * @return $this
     */
    public function setFontMetrics(FontMetrics $fontMetrics)
    {
        $this->fontMetrics = $fontMetrics;
        return $this;
    }

    /**
     * @return FontMetrics
     */
    public function getFontMetrics()
    {
        return $this->fontMetrics;
    }

    /**
     * Generate a string representation of the Style
     *
     * This dumps the entire property array into a string via print_r.  Useful
     * for debugging.
     *
     * @return string
     */
    /*DEBUGCSS print: see below additional debugging util*/
    function __toString()
    {
        $parent_font_size = $this->parent_style
            ? $this->parent_style->font_size
            : self::$default_font_size;

        return print_r(array_merge(["parent_font_size" => $parent_font_size ],
            $this->_props), true);
    }

    /*DEBUGCSS*/
    function debug_print()
    {
        $parent_font_size = $this->parent_style
            ? $this->parent_style->font_size
            : self::$default_font_size;

        print "    parent_font_size:" . $parent_font_size . ";\n";
        print "    Props [\n";
        print "      specified [\n";
        foreach ($this->_props as $prop => $val) {
            print '        ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
            if (isset($this->_important_props[$prop])) {
                print ' !important';
            }
            print ";\n";
        }
        print "      ]\n";
        print "      computed [\n";
        foreach ($this->_props_computed as $prop => $val) {
            print '        ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
            print ";\n";
        }
        print "      ]\n";
        print "      cached [\n";
        foreach ($this->_prop_cache as $prop => $val) {
            print '        ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true));
            print ";\n";
        }
        print "      ]\n";
        print "    ]\n";
    }
}