You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
22 KiB

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