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.

447 lines
16 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. % Extending Font resource category with CIDFont-CMap fonts.
  16. languagelevel 2 .setlanguagelevel currentglobal //true setglobal
  17. % In the comments below, 'CSI' is an abbreviation/acronym for CIDSystemInfo.
  18. % We pre-scan resource files to retrieve the CSI from them.
  19. % First we define a hidden procset .prs_dict containing
  20. % necessary variables and procedures.
  21. % Then we redefine the old /Font category using this procset.
  22. % We maintain internal caches for the CSI values retrieved from
  23. % resource files. This supposes that document doesn't uninstall
  24. % resource files. To disable caching, set enable_cache to false.
  25. % We assume that names starting with '.prs' do not appear in resource files.
  26. % If this causes any problem, this prefix should be systematically changed
  27. % in this file. ('prs' is an abbreviation for 'prescan'.)
  28. 25 dict begin
  29. % Define local variables :
  30. /.prs_dict currentdict def % self-reference (constant)
  31. /.prs_empty 0 dict readonly def
  32. /path_buffer 8192 string def
  33. /name_buffer 1024 string def
  34. /minus (-) 0 get def % character code constant for '-'
  35. /period (.) 0 get def % character code constant for '.'
  36. /CMap 10 dict def % CSI cache for CMaps
  37. /CIDFont 10 dict def % CSI cache for CIDFonts
  38. /enable_cache //true def % set false to disable cache
  39. % The folloving variables are just placeholders for ones to be set
  40. % dynamically :
  41. /.prsFile 0 def % file to prescan
  42. /.prsResult 0 def % result of prescan
  43. /.prsDictCount 0 def % save the dictionary stack depth
  44. % Define a dummy CIDInit procset to use while pre-scanning :
  45. /DummyCIDInit 15 dict
  46. begin
  47. /begincmap {} def
  48. /usecmap {pop} .internalbind def
  49. {stop} .internalbind
  50. [ /begincodespacerange /endcodespacerange /beginnotdefchar /endnotdefchar
  51. /beginnotdefrange /endnotdefrange /begincidchar /endcidchar /begincidrange
  52. /endcidrange /endcmap /usefont /StartData
  53. ] {
  54. 1 index def
  55. } .internalbind forall
  56. pop
  57. currentdict end def
  58. % Define a local 'findresource' for pre-scanning :
  59. % (it returns the dummy CIDInit instead of the regular CIDInit ProcSet)
  60. /findresource { % <InstName> <CatName> findresource <inst>
  61. 2 copy /ProcSet eq exch % /InstName /CatName bool /InstName
  62. /CIDInit eq and {
  63. pop pop //DummyCIDInit
  64. } {
  65. //findresource exec
  66. } ifelse
  67. } .internalbind def
  68. % Define procedures for pre-scanning :
  69. /StopIfCSIDefined { % - StopIfCSIDefined -
  70. % Check if the dictionary stack contains a dictionary containing /CIDSystemInfo.
  71. % The search is limited to the top .prsDictCount dictionaries in the stack.
  72. % If so, retrieve the CSI, and execute stop to terminate the pre-scanning of the file.
  73. % Otherwise, do nothing, so the pre-scanning continues.
  74. countdictstack //.prs_dict /.prsDictCount get sub dup {
  75. currentdict /CIDSystemInfo .knownget {
  76. //.prs_dict exch /.prsResult exch put
  77. stop
  78. } if
  79. currentdict exch end
  80. } repeat {
  81. begin
  82. } repeat
  83. } .internalbind def
  84. /PrescanFile { % - PrescanFile -
  85. { //.prs_dict /.prsFile get token {
  86. dup type % token type
  87. dup /nametype eq exch /operatortype eq or {
  88. dup xcheck {
  89. exec
  90. //StopIfCSIDefined exec
  91. } if
  92. } if
  93. } {
  94. stop
  95. } ifelse
  96. } loop
  97. } .internalbind odef
  98. /GetCIDSystemInfoFromFile { % <file> GetCIDSystemInfoFromFile <CSI>
  99. % This procedure reads resource files with 'token',
  100. % executing the tokens untill /CIDSystemInfo appears to be defined.
  101. % Normally the resource file creates a new dictionary on
  102. % dictionary stack and defines /CIDSystemInfo in it.
  103. %
  104. % Returns an empty dictionary if no CIDSystemInfo is found.
  105. RESMPDEBUG { (cidcm GetCIDSystemInfoFromFile beg) = } if
  106. //.prs_dict begin
  107. /.prsFile exch def
  108. /.prsResult //.prs_empty def
  109. /.prsDictCount countdictstack def
  110. RESMPDEBUG { (cidcm GetCIDSystemInfoFromFile will PrescanFile.) = } if
  111. { //PrescanFile } stopped pop
  112. //.prs_dict /.prsResult get
  113. end
  114. RESMPDEBUG { (cidcm GetCIDSystemInfoFromFile end) = } if
  115. } .internalbind def
  116. /GetCIDSystemInfo { % <InstName> <CatName> GetCIDSystemInfo <CSI>
  117. % Retrieve CSI, using caches.
  118. RESMPDEBUG { (cidcm GetCIDSystemInfo beg) = } if
  119. /Category findresource begin % /InstName
  120. dup ResourceStatus
  121. {
  122. pop 2 lt {
  123. FindResource /CIDSystemInfo .knownget not {
  124. //.prs_empty
  125. } if % CSI
  126. } { % /InstName
  127. currentdict /GetCIDSystemInfoFromMap .knownget {
  128. exec
  129. } if
  130. dup type /nametype eq
  131. {
  132. RESMPDEBUG { (cidcm GetCIDSystemInfo got a name.) = } if
  133. //.prs_dict Category get % /InstName CSIs
  134. dup 2 index known
  135. //enable_cache and {
  136. RESMPDEBUG { (cidcm GetCIDSystemInfo from cache.) = } if
  137. exch get % CSI
  138. } {
  139. RESMPDEBUG { (cidcm GetCIDSystemInfo from file.) = } if
  140. exch % CSIs /InstName
  141. dup //path_buffer ResourceFileName % CSIs /InstName (path)
  142. RESMPDEBUG { (cidcm GetCIDSystemInfo from file ) print dup = } if
  143. currentglobal exch //true setglobal % CSIs /InstName g (path)
  144. mark exch % CSIs /InstName g [ (path)
  145. { (r) file } stopped {
  146. cleartomark //.prs_empty
  147. } {
  148. exch 1 index % CSIs /InstName g file [ file
  149. //GetCIDSystemInfoFromFile stopped {
  150. cleartomark closefile //.prs_empty
  151. } {
  152. exch pop exch closefile
  153. } ifelse
  154. } ifelse % CSIs /InstName g CSI
  155. exch setglobal % CSIs /InstName CSI
  156. dup 4 1 roll % CSI CSIs /InstName CSI
  157. put % CSI
  158. RESMPDEBUG {
  159. (cidcm GetCIDSystemInfo got from file : <<) print
  160. dup { exch //=string cvs print ( ) print
  161. //=string cvs print ( ) print
  162. } forall
  163. (>>) =
  164. } if
  165. } ifelse
  166. } if
  167. } ifelse
  168. } {
  169. pop //.prs_empty
  170. } ifelse
  171. end
  172. RESMPDEBUG { (cidcm GetCIDSystemInfo end) = } if
  173. } .internalbind def
  174. /IsCompatibleCSI { % <CSI-M> <CSI-F> IsCompatibleCSI <bool>
  175. % The CSI in a CIDFont may be an array, a dict, or null.
  176. % If it is an array, it must be of 1 element, which is a dict.
  177. % In this case the dict is used for testing the compatibility.
  178. % Two dicts are compatible iff they contain same /Ordering and /Registry.
  179. % Identity CMap is compatible with any CIDFont.
  180. exch % CSI-F CSI-M
  181. { dup type /arraytype eq {
  182. dup length 1 ne {
  183. pop pop //false exit
  184. } if
  185. 0 get
  186. } if % CSI-F CSI-M
  187. dup type /dicttype ne {
  188. pop pop //false exit
  189. } if % CSI-F <<CSI-M>>
  190. exch % <<CSI-M>> CSI-F
  191. dup type /dicttype ne {
  192. pop pop //false exit
  193. } if % <<CSI-M>> <<CSI-F>>
  194. dup /Ordering .knownget {
  195. /Identity eq {
  196. pop pop //true exit
  197. } if
  198. } if
  199. //true % <<CSI-M>> <<CSI-F>> bEQ
  200. [/Registry /Ordering] {
  201. 2 index 1 index .knownget not {
  202. 1234567
  203. } if % <<CSI-M>> <<CSI-F>> bEQ /key vF
  204. exch % <<CSI-M>> <<CSI-F>> bEQ vF /key
  205. 4 index exch .knownget not {
  206. 7654321
  207. } if % <<CSI-M>> <<CSI-F>> bEQ vF vM
  208. eq and % <<CSI-M>> <<CSI-F>> bEQ
  209. } forall
  210. exch pop exch pop % bEQ
  211. exit
  212. } loop
  213. } .internalbind def
  214. /IsWellComposed { % <CIDFontName> <CMapName> IsWellComposed <bool>
  215. % Check if the given CIDFont and CMap have compatible CSIs.
  216. exch % /CMapName /CIDFontName
  217. /CIDFont //GetCIDSystemInfo exec % /CMapName CSI-F
  218. dup type /dicttype eq {
  219. dup length 0 ne {
  220. exch % CSI-F /CMapName
  221. /CMap //GetCIDSystemInfo exec % CSI-F CSI-M
  222. //IsCompatibleCSI exec % bool
  223. } {
  224. pop pop //false
  225. } ifelse
  226. } {
  227. pop pop //false
  228. } ifelse
  229. } .internalbind def
  230. /IsComposedFont { % <FontName> IsComposedFont <CIDFontName> <CMapName> true
  231. % <FontName> IsComposedFont false
  232. % Check if the given font name may be decomposed into CIDFont.CMap, CIDFont-CMap
  233. % or into CIDFont--CMap, such that CIDFont and CMap have compatible CSIs.
  234. % FontName
  235. dup type /stringtype ne {
  236. //name_buffer cvs
  237. } if % (FontName)
  238. { dup length 2 sub -1 1 {
  239. % (FontName) i
  240. 2 copy get dup //minus eq exch //period eq or {
  241. 2 copy 2 copy % (FontName) i (FontName) i (FontName) i
  242. 2 copy get //minus eq {
  243. 2 copy 1 sub get //minus eq {
  244. 1 sub
  245. } if
  246. } if % (FontName) i (FontName) i (FontName) i0
  247. 0 exch getinterval cvn % (FontName) i (FontName) i /CIDFontName
  248. 3 1 roll % (FontName) i /CIDFontName (FontName) i
  249. 1 add dup % (FontName) i /CIDFontName (FontName) i1 i1
  250. 5 index length % (FontName) i /CIDFontName (FontName) i1 i1 l
  251. exch sub getinterval cvn % (FontName) i /CIDFontName /CMapName
  252. 2 copy //IsWellComposed exec { % (FontName) i /CIDFontName /CMapName
  253. 4 2 roll pop pop % /CIDFontName /CMapName
  254. stop
  255. } if
  256. pop pop pop
  257. } {
  258. pop
  259. } ifelse % (FontName)
  260. } for
  261. pop
  262. } stopped
  263. } .internalbind def
  264. /ComposeName { % <CIDFont> <CMap> <scr> ComposeName <CIDFont-CMap>
  265. dup dup 5 2 roll % (scr) (scr) /CIDFont /CMap (scr)
  266. 3 2 roll exch cvs length dup % (scr) (scr) /CMap l0 l0
  267. 4 -1 roll exch //minus put % (scr) /CMap l0
  268. 1 add dup % (scr) /CMap l1 l1
  269. 3 index dup length % (scr) /CMap l1 l1 (scr) L
  270. 2 index sub % (scr) /CMap l1 l1 (scr) LT
  271. 3 2 roll % (scr) /CMap l1 (scr) LT l1
  272. exch getinterval % (scr) /CMap l1 (scrT)
  273. 3 2 roll exch cvs length % (scr) l1 l2
  274. add 0 exch getinterval % (CIDFont-CMap)
  275. } .internalbind def
  276. % Redefine the /Font category with CIDFont-CMap construction :
  277. % The following code supposes that the following names are not
  278. % defined in the old /Font category dictionary :
  279. % /IsComposedFont, /IsWellComposed .
  280. /Font /Category findresource dup length dict copy begin
  281. /FindResource { % <InstName> FindResource <inst>
  282. dup //ResourceStatus exec {
  283. pop pop //FindResource exec
  284. } {
  285. dup //IsComposedFont exec { % /FontName /CIDFontName /CMapName
  286. exch [ exch ] composefont % inst
  287. } {
  288. //FindResource exec
  289. } ifelse
  290. } ifelse
  291. } .internalbind def
  292. /ResourceStatus { % <InstName> ResourceStatus <nStatus> <nSize> true
  293. % <InstName> ResourceStatus false
  294. dup //ResourceStatus exec {
  295. 3 2 roll pop //true % nStatus nSize true
  296. } {
  297. //IsComposedFont exec { % /CIDFontName /CMapName
  298. /CMap resourcestatus { % /CIDFontName nStatusM nSizeM
  299. exch pop exch % nSizeM /CIDFontName
  300. /CIDFont resourcestatus { % nSizeM nStatusF nSizeF
  301. exch pop % nSizeF nSizeM
  302. dup 0 ge {
  303. exch dup 0 ge {
  304. add
  305. } {
  306. exch pop
  307. } ifelse
  308. } {
  309. pop
  310. } ifelse % nSize
  311. 2 exch //true % nStatus nSize true
  312. } {
  313. pop pop pop //false % work around buggy resource file
  314. } ifelse
  315. } {
  316. pop pop pop //false % work around buggy resource file
  317. } ifelse
  318. } {
  319. //false
  320. } ifelse
  321. } ifelse
  322. } .internalbind def
  323. /ResourceForAll { % <template> <proc> <scratch> ResourceForAll -
  324. % We suppose that the resourceforall procedure does not
  325. % define or install new fonts, CMaps, and/or CIDFonts.
  326. % First we create 3 temporary dictionaries to store temporary data
  327. % about fonts, CMaps and CIDFonts.
  328. % These dictionaries must be created dynamically, to allow for a possible
  329. % recursive call to resourceforall from the resourceforall procedure.
  330. currentglobal //false setglobal
  331. 20 dict 20 dict 20 dict % (templ) proc (scr) g <<CIDFont>> <<CMap>> <<Fonts>>
  332. % Store resource identifiers into local dictionaries
  333. % A resource instance can have a key that is not a name or a string. In this
  334. % case, resourceforall passes the key directly to proc instead of copying it
  335. % into the scratch string. This case can arise only for a resource instance
  336. % defined in virtual memory by a previous defineresource
  337. % Discard non-string keys of CIDFont and CMap because <CIDFontName>-<CMapName>
  338. % is only defined for names.
  339. { /.DisableResourceOrdering pop % gs_resmp accesses this through execstack - don't remove !
  340. 6 index [ 2 index {exch //null put} aload pop ] cvx bind 6 index //ResourceForAll exec
  341. (*) [ 3 index {exch dup type /stringtype eq { cvn dup put } { pop pop } ifelse } aload pop
  342. ] cvx bind 6 index /CMap resourceforall
  343. (*) [ 4 index {exch dup type /stringtype eq { cvn dup put } { pop pop } ifelse } aload pop
  344. ] cvx bind 6 index /CIDFont resourceforall
  345. exit
  346. } loop % This loop is a pattern for execstack_lookup - don't remove !
  347. 4 -1 roll setglobal % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
  348. %% Make the list of fonts in the form (/Name status) :
  349. % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
  350. dup {
  351. pop dup
  352. //ResourceStatus exec {
  353. pop 2 index % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>> /Name nStatus <<Font>>
  354. 3 1 roll put % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
  355. } {
  356. pop
  357. } ifelse
  358. } forall % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
  359. %% Add CIDFont-CMap to it (filtering duplicates) :
  360. 3 2 roll {
  361. 3 index {
  362. 3 1 roll % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap /CIDFont /CMap
  363. 6 index //ComposeName exec % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap (Font)
  364. dup 8 index .stringmatch {
  365. cvn % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap /Font
  366. dup 4 index exch known {
  367. pop pop
  368. } {
  369. 2 index % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap /Font /CIDFont
  370. 4 2 roll % (templ) proc (scr) <<CMap>> <<Font>> /Font /CIDFont /CIDFont /CMap
  371. //IsWellComposed exec {
  372. exch 2 index exch 2 put % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont
  373. } {
  374. exch pop
  375. } ifelse
  376. } ifelse
  377. } {
  378. pop pop
  379. } ifelse
  380. dup % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CIDFont
  381. } forall
  382. pop pop % (templ) proc (scr) <<CMap>> <<Font>>
  383. } forall % (templ) proc (scr) <<CMap>> <<Font>>
  384. exch pop % (templ) proc (scr) <<Font>>
  385. 4 3 roll pop % proc (scr) <<Font>>
  386. % Make the enumerator and apply it :
  387. /MappedCategoryRedefiner /ProcSet findresource /MakeResourceEnumerator get exec exec
  388. } .internalbind def
  389. currentdict end /Font exch /Category defineresource pop
  390. end
  391. setglobal .setlanguagelevel