472 lines
15 KiB
PostScript
472 lines
15 KiB
PostScript
% 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
|
|
|
|
and { % => 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
|
|
|
|
and { % => 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 |