File size: 15,569 Bytes
07c3cdd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
% 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