% Definition of float context "class" /context-add-absolute-positioned { % => Box Context dup /AbsolutePositioned get % => Box Context AB 2 index exch array-prepend % => Box Context AB' /AbsolutePositioned exch put % => Box pop } def /context-add-fixed-positioned { % => Box Context dup /FixedPositioned get % => Box Context AB 2 index exch array-prepend % => Box Context AB' /FixedPositioned exch put % => Box pop } def /context-add-float { % => Float Context dup /Floats get % => Float Context Floats dup 0 get % => Float Context Floats Floats[0] 3 index exch array-prepend % => Float Context Floats Floats[0]' 0 exch put % => Float Context pop pop } def /context-container-uid { % => Context /ContainerUID get 0 get } def /context-create { % => << /ContainerUID [1] /AbsolutePositioned [] /FixedPositioned [] /Floats [[]] /Margin [0 0] /Viewport [] >> } def % Find the minimal X at the given Y coordinate suitable for float placement % /context-float-left-x { % => Y X Context 3 copy dup context-floats % => Y X Context Y X Context Floats context-float-left-x-rec % => Y X Context X % Clear the stack exch pop exch pop exch pop } def /context-float-left-x-rec { % => Y X Context Floats dup length 0 gt { dup 0 get % => Y X Context Floats Float dup /float get-css-value /left eq { % Check if this float contains given Y-coordinate % % Note that top margin coordinate is inclusive but % bottom margin coordinate is exclusive! The cause is following: % - if we have several floats in one line, their top margin edge Y coordinates will be equal, % so we must use agreater or equal sign to avod placing all floats at one X coordinate % - on the other side, if we place one float under the other, the top margin Y coordinate % of bottom float will be equal to bottom margin Y coordinate of the top float and % we should NOT offset tho bottom float in this case dup get-top-margin % => Y X Context Floats Float FTM rounding-epsilon add 5 index % => Y X Context Floats Float FTM Y ge % => Y X Context Floats Float FTM>=Y 1 index get-bottom-margin % => Y X Context Floats Float FTM>=Y FBM 6 index % => Y X Context Floats Float FTM>=Y FBM Y lt % => Y X Context Floats Float FTM>=Y FBM Y X Context Floats Float dup get-right-margin % => Y X Context Floats Float FRM 4 index max % => Y X Context Floats Float X'=MAX(FRM,X) exch pop % => Y X Context Floats X' 4 3 roll pop % => Y Context Floats X' 3 1 roll % => Y X' Context Floats array-pop-first context-float-left-x-rec % => X } { pop array-pop-first context-float-left-x-rec } ifelse } { pop array-pop-first context-float-left-x-rec } ifelse } { % no more floats pop pop exch pop } ifelse } def % => X % Calculates position of left floating box (taking into account the possibility % of "wrapping" float to next line in case we have not enough space at current level (Y coordinate) % % @param parent reference to a parent box % @param width width of float being placed. Full width! so, extra horizontal space (padding, margins and borders) is added here too % @param $y Starting Y-coordinate % @return X X coordinate of float upper-left corner % @return Y Y coordinate of float upper-left corner % /context-float-left-xy { % => Parent Width Y Context % Prepare information about the float bottom corrdinates dup context-floats % => Parent Width Y Context Floats make-sorted-bottom-y-list % => Parent Width Y Context FloatBottoms context-float-left-xy-rec % => Y X } def % => Y X /context-float-left-xy-rec { % => Parent Width Y Context FloatBottoms 4 index get-left 3 index % => Parent Width Y Context FloatBottoms X Y exch % => Parent Width Y Context FloatBottoms Y X 3 index context-float-left-x % => Parent Width Y Context FloatBottoms X % Check if current float will fit into the parent box dup 5 index add % => Parent Width Y Context FloatBottoms X FloatRight 6 index get-right rounding-epsilon add le { % => Parent Width Y Context FloatBottoms X % will fit exch pop % => Parent Width Y Context X exch pop % => Parent Width Y X 4 2 roll % => Y X Parent Width pop pop exch % => X Y } { pop % => Parent Width Y Context FloatBottoms % Check if all floats have been already cleared dup length 0 eq { % All floats are cleared; fall back to the leftmost X coordinate pop pop exch pop % => Parent Y exch % => Y Parent get-left exch % => X Y } { % No, float does not fit at current level, let's try to 'clear' some previous floats dup 0 get % => Parent Width Y Context FloatBottoms Bottom0 3 index min % => Parent Width Y Context FloatBottoms Y' 4 3 roll pop % => Parent Width Context FloatBottoms Y' 3 1 roll array-pop-first % => Parent Width Y' Context FloatBottoms' context-float-left-xy-rec % => X Y } ifelse } ifelse } def % => X Y % Find the minimal X at the given Y coordinate suitable for float placement % /context-float-right-x { % => Y X Context 3 copy dup context-floats % => Y X Context Y X Context Floats context-float-right-x-rec % => Y X Context X % Clear the stack exch pop exch pop exch pop } def /context-float-right-x-rec { % => Y X Context Floats dup length 0 gt { dup 0 get % => Y X Context Floats Float dup /float get-css-value /right eq { % Check if this float contains given Y-coordinate % % Note that top margin coordinate is inclusive but % bottom margin coordinate is exclusive! The cause is following: % - if we have several floats in one line, their top margin edge Y coordinates will be equal, % so we must use agreater or equal sign to avod placing all floats at one X coordinate % - on the other side, if we place one float under the other, the top margin Y coordinate % of bottom float will be equal to bottom margin Y coordinate of the top float and % we should NOT offset tho bottom float in this case dup get-top-margin % => Y X Context Floats Float FTM rounding-epsilon add 5 index % => Y X Context Floats Float FTM Y ge % => Y X Context Floats Float FTM>=Y 1 index get-bottom-margin % => Y X Context Floats Float FTM>=Y FBM 6 index % => Y X Context Floats Float FTM>=Y FBM Y lt % => Y X Context Floats Float FTM>=Y FBM Y X Context Floats Float dup get-left-margin % => Y X Context Floats Float FRM 4 index min % => Y X Context Floats Float X'=MAX(FRM,X) exch pop % => Y X Context Floats X' 4 3 roll pop % => Y Context Floats X' 3 1 roll % => Y X' Context Floats array-pop-first context-float-right-x-rec % => X } { % => Y X Context Floats Float pop % => Y X Context Floats array-pop-first context-float-right-x-rec % => X } ifelse } { pop array-pop-first context-float-right-x-rec } ifelse } { % no more floats pop pop exch pop } ifelse } def % => X % Calculates position of left floating box (taking into account the possibility % of "wrapping" float to next line in case we have not enough space at current level (Y coordinate) % % @param parent reference to a parent box % @param width width of float being placed. Full width! so, extra horizontal space (padding, margins and borders) is added here too % @param $y Starting Y-coordinate % @return X X coordinate of float upper-left corner % @return Y Y coordinate of float upper-left corner % /context-float-right-xy { % => Parent Width Y Context % Prepare information about the float bottom corrdinates dup context-floats % => Parent Width Y Context Floats make-sorted-bottom-y-list % => Parent Width Y Context FloatBottoms context-float-right-xy-rec % => X Y } def % => X Y /context-float-right-xy-rec { % => Parent Width Y Context FloatBottoms 4 index get-right 3 index % => Parent Width Y Context FloatBottoms X Y exch 3 index context-float-right-x % => Parent Width Y Context FloatBottoms X % Check if current float will fit into the parent box dup % => Parent Width Y Context FloatBottoms X X 6 index get-right rounding-epsilon add % => Parent Width Y Context FloatBottoms X X FRight le { % => Parent Width Y Context FloatBottoms X % will fit exch pop exch pop % => Parent Width Y X 4 2 roll % => Y X Parent Width pop pop exch % => X Y } { pop % => Parent Width Y Context FloatBottoms % Check if all floats have been already cleared dup length 0 eq { % All floats are cleared; fall back to the leftmost X coordinate pop pop exch pop % => Parent Y exch % => Y Parent get-left exch % => X Y } { % No, float does not fit at current level, let's try to 'clear' some previous floats dup 0 get % => Parent Width Y Context FloatBottoms Bottom0 3 index min % => Parent Width Y Context FloatBottoms Y' 4 3 roll pop % => Parent Width Context FloatBottoms Y' 3 1 roll array-pop-first % => Parent Width Y' Context FloatBottoms' context-float-left-xy-rec % => X Y } ifelse } ifelse } def % => X Y /context-floats { % => Context /Floats get 0 get } def /context-get-absolute-positioned { % => Context /AbsolutePositioned get } def /context-get-collapsed-margin { % => Context /Margin get 0 get } def /context-get-fixed-positioned { % => Context /FixedPositioned get } def /context-get-viewport { % => Context /Viewport get 0 get } def /context-point-in-floats { % => Y X Context /null % => Y X Context /null 1 index context-floats % => Y X Context /null Floats { % => Y X Context /null Float 4 index 4 index 2 index box-generic-contains-point-margin { % => Y X Context /null Float exch pop exit % => Y X Context Float } if pop } forall % => Y X Context Float exch pop exch pop exch pop } def /context-pop { % => Context dup context-pop-collapsed-margin dup context-pop-floats pop } def /context-pop-collapsed-margin { % => Context dup /Margin get % => Context CMT array-pop-first % => Context CMT' /Margin exch put % => } def /context-pop-container-uid { % => Context dup /ContainerUID get array-pop-first /ContainerUID exch put } def /context-pop-floats { % => Context dup /Floats get array-pop-first /Floats exch put } def /context-pop-viewport { % => Context dup /Viewport get array-pop-first % => Context Viewports /Viewport exch put % => } def /context-push { % => Context 0 1 index context-push-collapsed-margin dup context-push-floats pop } def /context-push-collapsed-margin { % => Value Context dup /Margin get % => Value Context CMT 2 index exch % => Value Content Value CMT array-append % => Value Context CMT' /Margin exch put % => Value pop } def /context-push-container-uid { % => Uid Context dup /ContainerUID get % => Uid Context UIDStack 2 index exch array-append % => Uid Context UIDStack' 1 index exch /ContainerUID exch put pop pop } def /context-push-floats { % => Context dup /Floats get [] exch array-append % => Context Floats' /Floats exch % => Context /Floats Floats' put % => } def /context-push-viewport { % => Viewport Context dup /Viewport get % => Viewport Context Viewports 2 index exch array-append % => Viewport Context Viewports' 1 index exch /Viewport exch put % => Viewport Context pop pop } def % helper utility /make-sorted-bottom-y-list { % => Boxes { get-bottom-margin exch array-prepend } exch [] exch reduce % => UnsortedBottomsYs { gt } % => UnsortedBottomsYs GtFun array-sort % => SortedBottomYs } def %%%%%%%%%%%%%%%%%%%%% /empty-context { << /Floats [] /CollapsedMarginTop [0] >> } def /context-stack [ empty-context ] def /push-context { empty-context context-stack array-append /context-stack exch def } def /pop-context { context-stack array-pop-first /context-stack exch def } def /context-current { context-stack 0 get } def /context-floats-bottom { % => MaxValue { get-bottom-margin min } exch context-floats reduce } def /context-save-float { % => Float context-current /Floats get array-append context-current exch /Floats exch put } def % Get the bottom edge coordinate of the bottommost float in % current formatting context % % @return /null in case of no floats exists in current context % numeric coordinate value otherwise % /context-float-bottom { % => context-floats dup length 0 gt { { get-bottom-margin min } exch dup 0 get get-bottom-margin exch reduce } { pop /null } ifelse } def % Get the right edge coordinate of the rightmost float in % current formatting context % % @return null in case of no floats exists in current context % numeric coordinate value otherwise % /context-float-right { % => context-floats dup length 0 gt { { get-right-margin min } exch dup 0 get get-right-margin exch reduce } { pop /null } ifelse } def