Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
%    Copyright (C) 2000 artofcode LLC.  All rights reserved.
2
% 
3
% This software is provided AS-IS with no warranty, either express or
4
% implied.
5
% 
6
% This software is distributed under license and may not be copied,
7
% modified or distributed except as expressly authorized under the terms
8
% of the license contained in the file LICENSE in this distribution.
9
% 
10
% For more information about licensing, please refer to
11
% http://www.ghostscript.com/licensing/. For information on
12
% commercial licensing, go to http://www.artifex.com/licensing/ or
13
% contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
% San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
 
16
% $Id: gs_resmp.ps,v 1.11 2004/10/25 15:11:37 igor Exp $
17
% A procset to redefine a resource category with a resource map.
18
 
19
% Public entries :
20
 
21
% Redefine - a procedure for redefining a resource category with a map.
22
%    Methods for interpreting the resource map to be provided by client 
23
%    in the argument dictionary.
24
%
25
%    Note that the procedure Redefine is idempotential :
26
%    consequtive calls to it will not replace the category methods,
27
%    but will merge resource maps. If an interleaving redefinition
28
%    needs to cancel the idempotentity, it must remove the entry
29
%    /.IsRedefinedWithMap from the category dictionary.
30
 
31
% MakeResourceEnumerator - this procedure is useful for
32
%    redefining any category. It provides a proper order of instances
33
%    and proper stacks during resourceforall.
34
 
35
% BindWithCurrentdict - a procedure for generating temporary procedures 
36
%    from templates, binding them with a local dictionary.
37
 
38
% execstack_lookup - a procedure for communicating through the execution stack.
39
%    It allows for a callee to get an information from an indirect caller.
40
 
41
% The procedures are designed for exeution witout putting
42
% the procset instance onto the dictionary stack.
43
 
44
languagelevel 2 .setlanguagelevel
45
currentglobal true setglobal
46
 
47
/MappedCategoryRedefiner 10 dict begin % The procset.
48
 
49
currentpacking false setpacking
50
 
51
/InstanceEnumeratorPattern   %  - InstanceEnumeratorPattern ...
52
{  
53
  % This is a pattern for enumeration procedure to be built dynamically,
54
  % applying BindWithCurrentdict with a temporary dictionary.
55
  % The following names will be replaced with specific objects
56
  % during BindWithCurrentdict :
57
  % en_local_dict - a dictionary for storing the local integer variable 'status'.
58
  % scr - the scratch string argument of resourceforall;
59
  % proc - the procedure argument of resourceforall;
60
  % InstancesStatus - a dictionary that maps resource instance names to their status value;
61
  % Category - the category to be enumerated.
62
 
63
  % When this procedure is called from ResourceForAll, the category is the current dictionary.
64
  % We remove it from the dictionary stack before performing the enumeration
65
  % to provide the <proc> to write to the underlying dictionary,
66
  % and put it back after the enumeration is completed.
67
  end
68
  { 
69
 
70
      en_local_dict exch /status exch put
71
      InstancesStatus {                                            
72
        en_local_dict /status get eq {
73
          scr cvs                           % ... (Font)
74
          proc exec                         %
75
        } {
76
          pop
77
        } ifelse                            % ...
78
      } forall
79
    } for                                   % ...
80
  } stopped
81
  Category begin
82
  { stop } if
83
} bind def
84
 
85
% An auxiliary proc for BindWithCurrentdict :
86
/.BindAux    % <proc> BindAux <proc>
87
{ 0 exec
88
} bind def
89
 
90
setpacking
91
 
92
/BindWithCurrentdict     % <proc> BindWithCurrentdict <proc>
93
{  
94
  % Make a copy of the given procedure, binding in the values of all names
95
  % defined in currentdict.
96
  % Caution1 : this code cannot handle procedures that were already
97
  %            bound recursively.
98
  % Caution2 : this code don't bind packedarrays. This was done
99
  %            intentionally for a termination of the procedure tree.
100
 
101
  dup length array copy
102
  dup length 1 sub -1 0 {                      
103
    2 copy get                            % {precopy} i {elem}
104
    dup dup type /arraytype eq exch xcheck and {
105
                                          % {precopy} i {elem}
106
      //.BindAux exec                     % {precopy} i {elem_copy}
107
      2 index 3 1 roll put                % {precopy}
108
    } {
109
      dup dup type /nametype eq exch xcheck and {
110
                                          % {precopy} i {elem}
111
        currentdict exch .knownget {            
112
          2 index 3 1 roll put            % {precopy}
113
        } {                                            
114
          pop
115
        } ifelse
116
      } {
117
        pop pop
118
      } ifelse
119
    } ifelse                              % {precopy}
120
  } for                                   % {copy}
121
  cvx
122
} bind def
123
 
124
//.BindAux 0 //BindWithCurrentdict put   % bind the recursive call in 'Bind'.
125
 
126
/MakeResourceEnumerator   % <proc> <scr> <InstancesStatus> MakeResourceEnumerator <Enumerator>
127
{
128
  % Build the enumeration procedure :
129
 
130
  % Since the resourceforall procedure may leave values on the operand stack,
131
  % we cannot simply store the enumerator's local data on the stack.
132
  % We also cannot use a static dictionary to store local variables,
133
  % because of possible recursion in the resourceforall procedure.
134
  % To work around this, we create a copy of the enumeration procedure and
135
  % bind it dynamically with a temporary dictionary, which contains
136
  % local variables for the currently executing instance of resourceforall.
137
 
138
  currentdict                    % Category
139
  6 dict begin % the temporary dictionary
140
    /Category exch def           %
141
    /InstancesStatus exch def
142
    /scr exch def
143
    /proc exch def
144
    /en_local_dict currentdict def
145
    //InstanceEnumeratorPattern //BindWithCurrentdict exec     % Enumerator
146
    /status 0 def % variable for the current status to enumerate - do not bind with it !
147
  end
148
 
149
} bind def
150
 
151
/execstack_lookup     % <object> execstack_lookup <object1>
152
                      % <object> execstack_lookup null
153
{ % Checks whether execution stack contains a procedure starting with <object>,
154
  % and retrives the 2nd element of the procedure,
155
  % or null if the procedure was not found.
156
  %
157
  % Since 'execstack' actually renders subarrays of procedures,
158
  % the pattern for recognition must be like this :
159
  %
160
  %   { <object> <object1>
161
  %     CallSomething
162
  %   } loop
163
  %
164
  % The solution with 'loop' depends on how GS implements cycles,
165
  % so it must not appear in documents, which are required to be interpreter independent.
166
  % Any other type of cycles are also acceptable.
167
  % If no repitition is really needed, just insert 'exit' into its body.
168
  % If <object> <object1> are not needed for the caller, insert "pop pop" after them.
169
  % If <object1> is really unuseful, the pattern may be simplified :
170
  %
171
  %   { <object> pop
172
  %     CallSomething
173
  %     exit
174
  %   } loop
175
  %
176
  % It will retrieve 'pop' or 'null'.
177
  %
178
  % Note that 2 topmost execstack elements are the execstack_lookup procedure and its caller.
179
  % We don't check them.
180
 
181
  currentglobal false setglobal                    % <object> bGlobal
182
  countexecstack array execstack                   % <object> bGlobal [execstack]
183
  dup null exch                                    % <object> bGlobal [execstack] null [execstack]
184
  length 3 sub -1 0 {                              % <object> bGlobal [execstack] null i
185
    2 index exch get                               % <object> bGlobal [execstack] null proc
186
    dup type dup /packedarraytype eq exch /arraytype eq or {
187
      dup length 1 gt {                            % <object> bGlobal [execstack] null proc
188
        dup 0 get                                  % <object> bGlobal [execstack] null proc elem0
189
        5 index eq {                               % <object> bGlobal [execstack] null proc
190
          1 get                                    % <object> bGlobal [execstack] null object1
191
          exch pop exit                            % <object> bGlobal [execstack] object1
192
        } {
193
          pop
194
        } ifelse
195
      } {
196
        pop                                        % <object> bGlobal [execstack] false
197
      } ifelse
198
    } {
199
      pop                                          % <object> bGlobal [execstack] false
200
    } ifelse
201
  } for                                            % <object> bGlobal [execstack] bResult
202
  exch pop exch setglobal exch pop                 % bResult
203
} bind def
204
 
205
currentpacking false setpacking
206
/MethodsToRedefine 5 dict begin
207
 
208
    % Procedures in this dictionary really are patterns for new category methods.
209
    % The following names will be replaced with specific objects during BindWithCurrentdict :
210
    %   .map - the map dictionary;
211
    %   DefineResource, ResourceStatus, ResourceFileName, FindResource, ResourceForAll 
212
    %        - procedures from the original resource category.
213
 
214
    /FindResource  % <Name> FindResource <dict>
215
    { RESMPDEBUG { (resmp FindResource beg ) print dup = } if
216
      dup ResourceStatus exec {
217
        pop 2 lt
218
      } {
219
        false
220
      } ifelse                             % bInVirtualMemory
221
      { FindResource exec
222
      } {
223
        dup dup .map exch .knownget {      % /Name /Name <<record>>
224
          dup dup /RecordVirtualMethods get /IsActive get exec {
225
            1 index .getvminstance {       % /Name /Name <<record>> holder
226
              1 get 1 eq
227
            } {
228
              true
229
            } ifelse                       % /Name /Name <<record>> bStatusIs1
230
            4 1 roll                       % bStatusIs1 /Name /Name <<record>>
231
            dup /RecordVirtualMethods get /MakeInstance get exec
232
                                           % bStatusIs1 /Name /Name Instance size
233
            5 1 roll                       % size bStatusIs1 /Name /Name Instance
234
            DefineResource exec            % size bStatusIs1 /Name Instance
235
            % Make ResourceStatus to return correct values for this instance :
236
            % Hack: we replace status values in the instance holder :
237
            exch .getvminstance pop        % size bStatusIs1 Instance holder
238
            dup 5 -1 roll 2 exch put       % bStatusIs1 Instance holder
239
            3 2 roll {                     % Instance holder
240
              1 1 put                      % Instance
241
            } {
242
              pop
243
            } ifelse                       % Instance
244
          } {                              % /Name /Name <<record>>
245
            pop pop FindResource exec            
246
          } ifelse
247
        } {                                % /Name /Name
248
          pop FindResource exec            
249
        } ifelse
250
      } ifelse
251
      RESMPDEBUG { (resmp FindResource end) = } if
252
    } bind def
253
 
254
    /ResourceStatus   % <Name> ResourceStatus <status> <size> true
255
                      % <Name> ResourceStatus false
256
    { RESMPDEBUG { (resmp ResourceStatus beg ) print dup == } if
257
      dup ResourceStatus exec {            % /Name status size
258
        1 index 2 lt {
259
          % In VM - return with it.
260
          3 2 roll pop true
261
        } {
262
          % Not in VM.
263
          exch pop exch                    % size /Name
264
          dup .map exch .knownget {        % size /Name <<record>>
265
            dup dup /RecordVirtualMethods get /IsActive get exec {
266
              3 2 roll pop                 % /Name <<record>>
267
              dup /RecordVirtualMethods get /GetSize get exec 2 exch true
268
            } {                            % size /Name <<record>>
269
              pop pop 2 exch true
270
            } ifelse
271
          } {                              % size /Name
272
            pop 2 exch true
273
          } ifelse
274
        } ifelse
275
      } {                                  % /Name
276
        dup .map exch .knownget {          % /Name <<record>>
277
          dup dup /RecordVirtualMethods get /IsActive get exec {
278
            dup /RecordVirtualMethods get /GetSize get exec 2 exch true
279
          } {                              % /Name <<record>>
280
            pop pop false
281
          } ifelse
282
        } {                                % /Name
283
          pop false
284
        } ifelse
285
      } ifelse
286
      RESMPDEBUG { (resmp ResourceStatus end) = } if
287
    } bind def
288
 
289
    /ResourceFileName  % <Name> <scratch> ResourceFileName <string>
290
    { RESMPDEBUG { (resmp ResourceFileName beg ) print 1 index = } if
291
      exch                                                    % (scratch) /Name
292
      .map 1 index .knownget {                                % (scratch) /Name <<record>>
293
      	RESMPDEBUG { (resmp ResourceFileName : have a map record.) = } if
294
        dup dup /RecordVirtualMethods get /IsActive get exec {
295
          RESMPDEBUG { (resmp ResourceFileName : record is active.) = } if
296
          dup /RecordVirtualMethods get /GetFilePath get exec % (string)
297
          RESMPDEBUG { (resmp ResourceFileName : retrieving ) print dup = } if
298
        } {                                                   % (scratch) /Name <<record>>
299
          RESMPDEBUG { (resmp ResourceFileName : record is NOT active.) = } if
300
          pop exch ResourceFileName exec
301
          RESMPDEBUG { (resmp ResourceFileName : retrieving ) print dup = } if
302
        } ifelse
303
      } { 
304
        RESMPDEBUG { (resmp ResourceFileName : have NO map record.) = } if
305
        exch ResourceFileName exec
306
        RESMPDEBUG { (resmp ResourceFileName : retrieving ) print dup = } if
307
      } ifelse
308
      RESMPDEBUG { (resmp ResourceFileName end) = } if
309
    } bind def
310
 
311
    /ResourceForAll  % <template> <proc> <scratch> ResourceForAll -
312
    { RESMPDEBUG { (resmp ResourceForAll beg ) print 2 index = } if
313
      % Create InstancesStatus dictionary :
314
      20 dict % IS - Instances Status
315
      4 1 roll                            % <<IS>> (templ) {proc} (sctarch)
316
                                          % <<IS>> bOrder (templ) {proc} (sctarch)
317
      % Check if we are under another ResourceForAll :
318
      /.DisableResourceOrdering //execstack_lookup exec null eq 4 1 roll
319
 
320
      % Put underlying resources to the InstancesStatus dictionary :
321
      currentdict % the category
322
      begin % ResourceForAll removes it locally.
323
      2 index
324
      { cvn                               % <<IS>> bOrder (templ) {proc} (sctarch) /Name
325
        4 index {
326
          dup ResourceStatus exec {pop 6 index 3 1 roll put} {pop} ifelse
327
        } {
328
          5 index exch 2 put % Don't need the ordering, put '2' as a scratch.
329
        } ifelse
330
      }
331
      2 index ResourceForAll exec         % <<IS>> bOrder (templ) {proc} (sctarch)
332
      4 3 roll pop                        % <<IS>> (templ) {proc} (sctarch)
333
      end
334
 
335
      % Put .map entries to the InstancesStatus dictionary :
336
      4 -1 roll begin                     % (templ) {proc} (sctarch)
337
      .map {                              % (templ) {proc} (sctarch) /Name record
338
         dup dup /RecordVirtualMethods get /IsActive get exec {
339
           pop                            % (templ) {proc} (sctarch) /Name
340
           dup currentdict exch known {
341
             pop
342
           } {
343
             dup 2 index cvs              % (templ) {proc} (sctarch) /Name (Name)
344
             4 index .stringmatch {       % (templ) {proc} (sctarch) /Name
345
               2 def % It is not in VM.
346
             } {
347
               pop
348
             } ifelse
349
           } ifelse
350
        } {                               % (templ) {proc} (sctarch) /Name record
351
          pop pop
352
        } ifelse
353
      } forall                            % (templ) {proc} (sctarch)
354
 
355
      % prepare stacks for the enumeration :
356
      3 2 roll pop                        % {proc} (sctarch)
357
      currentdict end                     % {proc} (scratch) <<IS>>
358
 
359
      % Make the enumerator and apply it :
360
      //MakeResourceEnumerator exec exec
361
      RESMPDEBUG { (resmp ResourceForAll end)= } if
362
    } bind def
363
 
364
    /GetCIDSystemInfoFromMap   % <Name> GetCIDSystemInfoFromMap <Name>
365
                               % <Name> GetCIDSystemInfoFromMap <dict>
366
    { RESMPDEBUG { (resmp GetCIDSystemInfoFromMap beg ) print dup = } if
367
      % This is a special function for communicating with GetCIDSystemInfo in gs_cidcm.ps .
368
      dup .map exch .knownget {
369
        RESMPDEBUG { (resmp GetCIDSystemInfoFromMap : have a map record.) = } if
370
        dup /RecordVirtualMethods get /GetCSI get exec
371
        dup null ne {
372
          RESMPDEBUG { (resmp GetCIDSystemInfoFromMap : retrieving a dict.) = } if
373
          exch
374
        } if
375
        pop
376
      } if
377
      RESMPDEBUG { (resmp GetCIDSystemInfoFromMap end) = } if
378
    } bind def
379
 
380
currentdict end def
381
setpacking
382
 
383
/Redefine     % <OptionsDict> Redefine -
384
{ % Before calling this proc, the OptionsDict must specify options for
385
  % the catregory to be redefined :
386
  % CategoryName - a name of category to redefine;
387
  % MapFileName - a string for the resource map file name;
388
  % VerifyMap - a procedure :
389
  %   <raw_map> VerifyMap -
390
  %   - checks the map for consistency
391
  % PreprocessRecord  - a procedure :
392
  %   <map> <Name> <raw_record> PreprocessRecord <map> <Name> <record> true
393
  %   <map> <Name> <raw_record> PreprocessRecord <map> <Name> <raw_record> false
394
  %   - converts a map record into a dictionary;
395
  %   It must add RecordVirtualMethods dictionary to the record :
396
  %     MakeInstance - a procedure :
397
  %         <Name> <record> MakeInstance <Name> <Instance> <size>
398
  %         - converts the record to resource instance;
399
  %     GetFilePath - a procedure for ResourceFileName :
400
  %         <scratch> <Name> <record> GetFilePath <filepath>
401
  %     GetSize - a procedure for ResourceStatus :
402
  %         <Name> <record> GetSize <size>
403
  %     GetCSI - a procedure for obtaining CIDSystemInfo dictionary from the record :
404
  %         <record> GetCSI <CSI>
405
  %         <record> GetCSI null
406
  %     IsActive - a procedure for skipping records depending on the current device :
407
  %         <record> IsActive <bool>
408
  %     Also it is allowed to contain additional entries for client's needs.
409
  % The OptionsDict is also used for storing some local variables.
410
 
411
  % If a category is being redefined several times with this function,
412
  % each redefinition must either use an unique map file,
413
  % or the map file should be scanned by the last redefinition
414
  % (and must be defined in the last one with /MapFileName).
415
  % This happens so because we must accumulate all variants of
416
  % methods before scanning the map. We would like to delay
417
  % the scanning until all redefinitions are done, but it requires
418
  % to implement a queue of "refinish" methods and execute it
419
  % at very end of the prelude.
420
 
421
  begin % OptionsDict
422
  CategoryName /Category findresource /OldCategory exch def
423
  OldCategory /.IsRedefinedWithMap known {
424
    % Already redefined with map - don't redefine, but enhance the map.
425
    OldCategory /NewCategory exch def
426
  } {
427
    % Redefine with a new category instance.
428
    OldCategory dup length dict 
429
    dup /.PreprocessRecord 4 dict put
430
    copy /NewCategory exch def
431
  } ifelse
432
 
433
  % Provide the 'or' logic for PreprocessRecord,
434
  % to allow different record types to be mixed in a single map file.
435
  % We do this with building a dictionary of PreprocessRecord procedures,
436
  % which come from different calls to Redefine :
437
  NewCategory /.PreprocessRecord get dup length % <<pr>> l  
438
  currentdict /PreprocessRecord get .growput
439
 
440
  currentdict /MapFileName known {
441
    MapFileName .libfile {
442
      1 dict begin
443
      /; {} def
444
      mark exch cvx exec .dicttomark           % <<map>>
445
      end
446
      dup VerifyMap                            % <<map>>
447
    } {
448
      QUIET not {
449
        currentdict /IsMapFileOptional .knownget not { false } if not {
450
          (Warning: the map file ) print dup =string cvs print ( was not found.) =
451
        } if
452
      } if
453
      pop 0 dict                               % <<map>>
454
    } ifelse
455
  } {
456
    currentdict /.map .knownget not { 
457
 
458
    } if
459
  } ifelse
460
 
461
  % Preprocess entries :
462
  dup NewCategory /.PreprocessRecord get       % <<map>> <<map>> <<pr>>
463
  3 1 roll {                                   % <<pr>> <<map>> /Name raw_record
464
    false 3 1 roll                             % <<pr>> <<map>> false /Name raw_record 
465
    4 index {                                  % <<pr>> <<map>> false /Name raw_record i {pr}
466
      exch pop                                 % <<pr>> <<map>> false /Name raw_record {pr}
467
      exec {                                   % <<pr>> <<map>> false /Name record
468
        3 -1 roll pop true 3 1 roll            % <<pr>> <<map>> true /Name record
469
        exit
470
      } if                                     % <<pr>> <<map>> false /Name raw_record
471
    } forall
472
    3 2 roll {                                 % <<pr>> <<map>> /Name record
473
      2 index 3 1 roll put                     % <<pr>> <<map>>
474
    } {
475
      exch                                     % <<pr>> <<map>> raw_record /Name
476
      (Incorrect record ) print =string cvs print ( of the map file ) print MapFileName =string cvs print (.) =
477
      end % Pops OptionsDict from dstack.
478
      pop pop pop                              %
479
      /Redefine cvx /undefinedresource signalerror
480
    } ifelse
481
  } forall                                     % <<pr>> <<map>>
482
  exch pop                                     % <<map>>
483
 
484
 
485
  % Add the map :
486
  OldCategory /.IsRedefinedWithMap known {     % <<map>>
487
    % Just add to the old map :
488
    OldCategory /.map get copy pop             %
489
  } {                                          % <<map>>
490
    % Store the map to both the category and OptionsDict :
491
    dup NewCategory exch /.map exch put
492
    /.map exch def                             %
493
  } ifelse
494
  OldCategory /.IsRedefinedWithMap known not {
495
    % Copy old methods to OptionsDict :
496
    [ /DefineResource /ResourceStatus /ResourceFileName 
497
      /FindResource /ResourceForAll
498
    ] {
499
      dup OldCategory exch get def
500
    } forall
501
 
502
    % Build new methods :
503
    //MethodsToRedefine {
504
      //BindWithCurrentdict exec NewCategory 3 1 roll put  
505
    } forall
506
    CategoryName /CIDFont ne {
507
      NewCategory /GetCIDSystemInfoFromMap undef
508
      % This is some ugly, sorry.
509
    } if
510
    % Redefine the category :
511
    NewCategory /.IsRedefinedWithMap true put
512
    CategoryName NewCategory /Category defineresource pop
513
  } if
514
  end % OptionsDict
515
} bind executeonly def
516
 
517
currentdict /PutPreprocessRecord .undef
518
 
519
currentdict end
520
/ProcSet defineresource pop
521
 
522
setglobal .setlanguagelevel