Source code of Windows XP (NT5)
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.

2375 lines
64 KiB

  1. /***
  2. **
  3. ** Module: Hints
  4. **
  5. ** Description:
  6. ** This is a module of the T1 to TT font converter. This is a
  7. ** sub-module of the T1 to TT data translator module. It deals
  8. ** with hints. Any part pf the T1 font that gets translated into
  9. ** TrueType instructions is done within this module.
  10. **
  11. ** Author: Michael Jansson
  12. **
  13. ** Created: 8/24/93
  14. **
  15. ***/
  16. /**** INCLUDES */
  17. /* General types and definitions. */
  18. #include <limits.h>
  19. #include <string.h>
  20. /* Special types and definitions. */
  21. #include "titott.h"
  22. #include "trig.h"
  23. #include "types.h"
  24. #include "safemem.h"
  25. #include "metrics.h"
  26. #include "t1msg.h"
  27. /* Module dependent types and prototypes. */
  28. #include "trans.h"
  29. #include "hints.h"
  30. #include "ttprog.h"
  31. /***** CONSTANTS */
  32. #define VERSION_SELECTOR 1 /* GetInfo[] selector for version number. */
  33. #define VERSION_1_5 33 /* Version 1.5 of Windows TrueType rasterizer. */
  34. #define STEMSNAPARGS 6 /* Number of args of the CreateStem TTFUN. */
  35. #ifdef SYMETRICAL_REDUCTION
  36. #define MIN_REDUCTION 4 /* Min reduction of the diag. cntrl. */
  37. #endif
  38. #define REDUCTION_C1 10 /* Min reduction, second method. */
  39. #define STACKINC 500 /* Stack increment for arg-stack + prep. */
  40. #define TARGSIZE 100 /* Size of temporary argument stack. */
  41. #define TTFLEXSIZE 9 /* Largest size of a flex, w/o the points. */
  42. #define TMP_TWILIGHTS 2
  43. #define TWILIGHTS_PER_STEM 4
  44. #define LEFTSTEM 1
  45. #define RIGHTSTEM 2
  46. #define SECONDPAIR 2
  47. #define MAXRANGE 15
  48. #define MAXEXTR 60 /* Max num of IP buckets. */
  49. #define UNDEF -1
  50. #define STDV_CVT 1
  51. #define STDH_CVT 2
  52. #define SNAPV_CVT(v) (v+3)
  53. #define SNAPH_CVT(t1m, v) (t1m->snapv_cnt+3+v)
  54. /* External leading hint programs. */
  55. static const UBYTE roman_hints[] = {
  56. /* Magic cookie. */
  57. op_pushb1 + 4, 66, 3, 8, 2, 16,
  58. op_clear,
  59. op_svcta | SUBOP_Y,
  60. op_pushb1, 3,
  61. /* Push 2pnt, in sub-pels. */
  62. op_mppem,
  63. op_mps,
  64. op_div,
  65. op_pushb1, 128,
  66. op_mul,
  67. /* Push InternalLeading, in sub-pels. */
  68. op_pushb1+1, 2, 1,
  69. op_md,
  70. op_sub,
  71. /* Push MAX(2pnt - i-leading, 0) */
  72. op_pushb1, 0,
  73. op_max,
  74. /* Add the external leading to the Ascent height. */
  75. op_shpix,
  76. };
  77. static const UBYTE swiss_hints[] = {
  78. /* Magic cookie. */
  79. op_pushb1 + 4, 66, 3, 8, 2, 16,
  80. op_clear,
  81. op_svcta | SUBOP_Y,
  82. op_pushb1, 3,
  83. /* 0<=height<=12.5 */
  84. op_mps,
  85. op_pushw1, HIBYTE(800), LOBYTE(800), /* 12.5 pnt */
  86. op_gt,
  87. op_if,
  88. /* Push 2pnt, in sub-pels. */
  89. op_mppem,
  90. op_mps,
  91. op_div,
  92. op_pushb1, 128,
  93. op_mul,
  94. op_else,
  95. /* 12.5 < height <= 13.5 */
  96. op_mps,
  97. op_pushw1, HIBYTE(864), LOBYTE(864), /* 13.5 pnt */
  98. op_gt,
  99. op_if,
  100. /* Push 3pnt, in sub-pels. */
  101. op_mppem, op_pushb1, 1, op_div,
  102. op_mps,
  103. op_div,
  104. op_pushb1, 192,
  105. op_mul,
  106. op_else,
  107. /* Push 4pnt, in sub-pels. */
  108. op_mppem, op_pushb1, 1, op_div,
  109. op_mps,
  110. op_div,
  111. op_pushw1, HIBYTE(256), /* LOBYTE(256) */ 0,
  112. op_mul,
  113. op_eif,
  114. op_eif,
  115. /* Push InternalLeading, in sub-pels. */
  116. op_pushb1+1, 2, 1,
  117. op_md,
  118. op_sub,
  119. op_dup,
  120. /* Push MAX(?pnt - i-leading, 0) */
  121. op_pushb1, 0,
  122. op_max,
  123. /* Add the external leading to the Ascent height. */
  124. op_shpix,
  125. };
  126. /* Pre-program. */
  127. static const UBYTE PrepProg[] = {
  128. op_pushw1, 0x01, 0xff, op_scanctrl,
  129. op_pushb1, 1, op_rcvt,
  130. op_pushb1, 128,
  131. op_lt,
  132. op_if,
  133. op_pushb1 + 1, 4, 0, op_scantype, op_scantype,
  134. op_else,
  135. op_pushb1 + 1, 5, 1, op_scantype, op_scantype,
  136. op_eif,
  137. };
  138. /***** LOCAL TYPES */
  139. /* Used for associating points to stems. */
  140. typedef struct {
  141. short from;
  142. short to;
  143. } Range;
  144. /* Zone bucket - Used for grid fitting a stem that may have
  145. been divided into several stem instructions due to hint replacement. */
  146. typedef struct TTStem {
  147. funit side1;
  148. funit side2;
  149. short rp1;
  150. short rp2;
  151. short ref;
  152. enum aligntype align;
  153. Range range[MAXRANGE];
  154. short cnt;
  155. } TTStem;
  156. /***** MACROS */
  157. /* General macros. */
  158. #define Trans3X TransX
  159. #define TransRX TransY
  160. #define CLOSETO(v1, v2, eps) (ABS((v1)-(v2))<=eps)
  161. #define CHECK_ARGSIZE(args, ta, num, asize) /* Check argument stack. */ \
  162. /*lint -e571 -e644 */if (((ta)+(int)(num))>(asize)) { \
  163. short *newarg = NULL;\
  164. if ((newarg = Realloc(args, sizeof(short)*(USHORT)(ta+num+STACKINC)))==NULL) { \
  165. Free(args); \
  166. LogError(MSG_ERROR, MSG_NOMEM, NULL);\
  167. return 0;\
  168. } else {\
  169. args = newarg;\
  170. asize = (short)(ta+num+STACKINC);\
  171. /*line +e571 +e644 */ }\
  172. }
  173. #define CHECK_PREPSIZE(prep, tp, num, psize) /* Check prep size. */ \
  174. if (((tp)+(num))>(psize)) { \
  175. UBYTE *newprep = NULL;\
  176. if ((newprep = Realloc(prep, tp+num+STACKINC))==NULL) { \
  177. Free(prep); \
  178. LogError(MSG_ERROR, MSG_NOMEM, NULL);\
  179. return 0;\
  180. } else {\
  181. prep = newprep;\
  182. psize = (short)(tp+num+STACKINC);\
  183. }\
  184. }
  185. /***** STATIC FUNCTIONS */
  186. /***
  187. ** Function: ConvertFlex
  188. **
  189. ** Description:
  190. ** Convert a T1 flex hint into a TrueType IP[]
  191. ** intruction sequence that will reduce a flex
  192. ** that is flatter than a given height.
  193. ***/
  194. static errcode ConvertFlex(const struct T1Metrics *t1m,
  195. const Flex *flexRoot,
  196. const short *ttpnts,
  197. UBYTE *pgm,
  198. short *pc,
  199. short *args,
  200. short *pcd,
  201. short *marg)
  202. {
  203. errcode status = SUCCESS;
  204. int cis, last_cis = UNDEF;
  205. char dir, last_dir = 0;
  206. short targ[TARGSIZE];
  207. funit height, diff;
  208. const Flex *flex;
  209. short ta = 0;
  210. int num = 0;
  211. /* Return to the glyph zone. */
  212. if (flexRoot) {
  213. pgm[(*pc)++] = op_szps;
  214. args[(*pcd)++] = 1;
  215. }
  216. for (flex=flexRoot; flex; flex=flex->next) {
  217. /* Points lost in ConvertOutline? */
  218. if (ttpnts[flex->start]==UNDEF ||
  219. ttpnts[flex->mid]==UNDEF ||
  220. ttpnts[flex->end]==UNDEF) {
  221. LogError(MSG_WARNING, MSG_FLEX, NULL);
  222. continue;
  223. }
  224. /* Vertical or horizontal flex? */
  225. if (ABS(flex->midpos.x-flex->pos.x) <
  226. ABS(flex->midpos.y-flex->pos.y)) {
  227. dir = SUBOP_Y;
  228. height = TransY(t1m, (funit)(flex->startpos.y - flex->pos.y));
  229. diff = TransY(t1m, (funit)(flex->midpos.y - flex->startpos.y));
  230. } else {
  231. dir = SUBOP_X;
  232. height = TransX(t1m, (funit)(flex->startpos.x - flex->pos.x));
  233. diff = TransX(t1m, (funit)(flex->midpos.x - flex->startpos.x));
  234. }
  235. /* Skip flex without depth. */
  236. if (diff==0)
  237. continue;
  238. cis = (int)((long)flex->civ * (long)GetUPEM(t1m) / 100 / ABS(diff));
  239. if (cis!=last_cis || dir!=last_dir ||
  240. (ta+TTFLEXSIZE+(ttpnts[flex->end]-ttpnts[flex->start]))>=TARGSIZE) {
  241. if (last_cis!=UNDEF) {
  242. AssembleArgs(targ, ta, pgm, pc);
  243. while(num--)
  244. pgm[(*pc)++] = op_call;
  245. pgm[(*pc)++] = op_eif;
  246. ta = 0;
  247. }
  248. pgm[(*pc)++] = (UBYTE)(op_svcta | dir);
  249. pgm[(*pc)++] = op_mppem;
  250. pgm[(*pc)++] = op_gt;
  251. pgm[(*pc)++] = op_if;
  252. args[(*pcd)++] = (short)(cis+1);
  253. num = 0;
  254. }
  255. status = EmitFlex(targ, &ta, height,
  256. ttpnts[flex->start],
  257. ttpnts[flex->mid],
  258. ttpnts[flex->end]);
  259. last_dir = dir;
  260. last_cis = cis;
  261. num++;
  262. if (status!=SUCCESS) {
  263. SetError(status = TTSTACK);
  264. break;
  265. }
  266. }
  267. if (num) {
  268. AssembleArgs(targ, ta, pgm, pc);
  269. while(num--)
  270. pgm[(*pc)++] = op_call;
  271. pgm[(*pc)++] = op_eif;
  272. }
  273. if ((*marg)<2)
  274. (*marg) = 2;
  275. return status;
  276. }
  277. /***
  278. ** Function: GetSnapV
  279. **
  280. ** Description:
  281. ** Return the closest snap width entry.
  282. ***/
  283. static short GetSnapV(const struct T1Metrics *t1m, const funit width)
  284. {
  285. USHORT dist = SHRT_MAX;
  286. USHORT j = 0;
  287. USHORT i;
  288. for (i=0; i<t1m->snapv_cnt; i++) {
  289. if (ABS(width-t1m->stemsnapv[i])<(short)dist) {
  290. dist = (USHORT)ABS(width-t1m->stemsnapv[i]);
  291. j = i;
  292. }
  293. }
  294. if (dist==SHRT_MAX)
  295. return UNDEF;
  296. return (short)j;
  297. }
  298. /***
  299. ** Function: GetSnapH
  300. **
  301. ** Description:
  302. ** Return the closest snap width entry.
  303. ***/
  304. static short GetSnapH(const struct T1Metrics *t1m, const funit width)
  305. {
  306. USHORT dist = SHRT_MAX;
  307. USHORT j = 0;
  308. USHORT i;
  309. for (i=0; i<t1m->snaph_cnt; i++) {
  310. if (ABS(width-t1m->stemsnaph[i])<(short)dist) {
  311. dist = (USHORT)ABS(width-t1m->stemsnaph[i]);
  312. j = i;
  313. }
  314. }
  315. if (dist==SHRT_MAX)
  316. return UNDEF;
  317. return (short)j;
  318. }
  319. /***
  320. ** Function: PosX
  321. **
  322. ** Description:
  323. ** This is a call-back function used by
  324. ** Interpolate.
  325. ***/
  326. static funit PosX(const Point pnt)
  327. {
  328. return pnt.x;
  329. }
  330. /***
  331. ** Function: PosY
  332. **
  333. ** Description:
  334. ** This is a call-back function used by
  335. ** Interpolate.
  336. ***/
  337. static funit PosY(const Point pnt)
  338. {
  339. return pnt.y;
  340. }
  341. /***
  342. ** Function: InRange
  343. **
  344. ** Description:
  345. ** This is function determines if a point is
  346. ** within range of a hint zone.
  347. ***/
  348. static boolean InRange(const short pnt, const Range *range, const short cnt)
  349. {
  350. short k;
  351. for (k=0; k<cnt; k++) {
  352. if ((range[k].from<=pnt) &&
  353. (range[k].to>=pnt || range[k].to==ENDOFPATH))
  354. break;
  355. }
  356. return (boolean)(k != cnt);
  357. }
  358. /***
  359. ** Function: BoundingStems
  360. **
  361. ** Description:
  362. ** Determines what stems are located to the
  363. ** left and to the right of a point on the
  364. ** outline, given its position.
  365. **
  366. ***/
  367. static short BoundingStems(short pnt, const short max_pnt,
  368. const funit pos, const TTStem *stems,
  369. const short cnt,
  370. short *left, short *right)
  371. {
  372. funit min, max;
  373. short i;
  374. max = SHRT_MAX;
  375. min = 1-SHRT_MAX;
  376. (*right) = UNDEF;
  377. (*left) = UNDEF;
  378. do {
  379. for (i=0; i<cnt; i++) {
  380. /* Is stem to the left and defined for the point? */
  381. if ((stems[i].side1<=pos) &&
  382. (stems[i].side1>min) &&
  383. InRange(pnt, stems[i].range, stems[i].cnt)) {
  384. min = stems[i].side1;
  385. (*left) = (short)i;
  386. }
  387. /* Is stem to the right and defined for the point. */
  388. if ((stems[i].side2>=pos) &&
  389. (stems[i].side2<max) &&
  390. InRange(pnt, stems[i].range, stems[i].cnt)) {
  391. max = stems[i].side2;
  392. (*right) = (short)i;
  393. }
  394. }
  395. /* Advance to the next point on the outline if we did not find stems. */
  396. } while (((*left)==UNDEF) && ((*right)==UNDEF) && (++pnt<(short)max_pnt));
  397. return pnt;
  398. }
  399. /***
  400. ** Function: EndOfRegion
  401. **
  402. ** Description:
  403. ** Determine what is the closest point, after the
  404. ** given point, for a new hint replacement.
  405. **
  406. ***/
  407. static short EndOfRegion(const short pnt, const TTStem *stem)
  408. {
  409. short k;
  410. for (k=0; k<stem->cnt; k++) {
  411. if ((stem->range[k].from<=pnt) &&
  412. (stem->range[k].to>=pnt || stem->range[k].to==ENDOFPATH))
  413. break;
  414. }
  415. return (short)((k==stem->cnt || stem->range[k].to==ENDOFPATH)
  416. ? SHRT_MAX : stem->range[k].to);
  417. }
  418. /***
  419. ** Function: AddToBucket
  420. **
  421. ** Description:
  422. ** This function will add a point, that
  423. ** is located between two stems, into a
  424. ** bucket that represents an interpolation
  425. ** zone.
  426. ***/
  427. static short AddToBucket(Extremas *extr,
  428. short xcnt,
  429. const short pnt,
  430. const funit left,
  431. const funit right,
  432. const TTStem *stems)
  433. {
  434. short rp1, rp2;
  435. short tmp, j;
  436. /* Pick the reference points (which are located in the twilight zone). */
  437. if (left!=UNDEF)
  438. rp1 = stems[left].rp2;
  439. else
  440. rp1 = UNDEF;
  441. if (right!=UNDEF)
  442. rp2 = stems[right].rp1;
  443. else
  444. rp2 = UNDEF;
  445. /* Normalize the reference points. */
  446. tmp = rp1;
  447. rp1 = (short)MIN(rp1, rp2);
  448. rp2 = (short)MAX(tmp, rp2);
  449. /* Create/Fill IP bucket. */
  450. for (j=0; j<xcnt; j++)
  451. if (extr[j].rp1==rp1 && extr[j].rp2==rp2 && extr[j].n<MAXPTS)
  452. break;
  453. if (j==xcnt) {
  454. if (xcnt<MAXEXTR) {
  455. extr[xcnt].rp1 = rp1;
  456. extr[xcnt].rp2 = rp2;
  457. extr[xcnt].n = 0;
  458. xcnt++;
  459. } else {
  460. LogError(MSG_WARNING, MSG_EXTREME1, NULL);
  461. }
  462. }
  463. /* Add the point to the bucket. */
  464. if (j<MAXEXTR && extr[j].n<MAXPTS &&
  465. (extr[j].pts[extr[j].n] = pnt)!=UNDEF)
  466. extr[j].n++;
  467. return xcnt;
  468. }
  469. /***
  470. ** Function: AddSidePntToBucket
  471. **
  472. ** Description:
  473. ** Same as AddToBucket, but the points are
  474. ** known to reside exactly on the side of
  475. ** a stem, and should be controled by one
  476. ** reference point alone. This is only needed
  477. ** for sheared fonts, where controling side
  478. ** point w.r.t. two reference poins leads
  479. ** to problems.
  480. ***/
  481. static short AddSidePntToBucket(Extremas *extr,
  482. short xcnt,
  483. const short pnt,
  484. const short rp)
  485. {
  486. short j;
  487. /* Create/Fill IP bucket. */
  488. for (j=0; j<xcnt; j++)
  489. if (extr[j].rp1==rp && extr[j].rp2==UNDEF && extr[j].n<MAXPTS)
  490. break;
  491. if (j==xcnt) {
  492. if (xcnt<MAXEXTR) {
  493. extr[xcnt].rp1 = rp;
  494. extr[xcnt].rp2 = UNDEF;
  495. extr[xcnt].n = 0;
  496. xcnt++;
  497. } else {
  498. LogError(MSG_WARNING, MSG_EXTREME1, NULL);
  499. }
  500. }
  501. /* Add the point to the bucket. */
  502. if (j<MAXEXTR && extr[j].n<MAXPTS &&
  503. (extr[j].pts[extr[j].n] = pnt)!=UNDEF)
  504. extr[j].n++;
  505. return xcnt;
  506. }
  507. /***
  508. ** Function: PickSides
  509. **
  510. ** Description:
  511. ** Select the position of the left and
  512. ** right side boundry of a point, given
  513. ** the stem to the left and right of the
  514. ** current point on the outline.
  515. ***/
  516. static void PickSides(short left, short right,
  517. funit *left_side,
  518. funit *right_side,
  519. TTStem *stems)
  520. {
  521. if (left!=right) {
  522. if (left!=UNDEF)
  523. (*left_side) = stems[left].side2;
  524. else
  525. (*left_side) = 1-SHRT_MAX/2;
  526. if (right!=UNDEF)
  527. (*right_side) = stems[right].side1;
  528. else
  529. (*right_side) = SHRT_MAX/2;
  530. } else {
  531. (*left_side) = stems[left].side1;
  532. (*right_side) = stems[right].side2;
  533. }
  534. }
  535. /***
  536. ** Function: PickSequence
  537. **
  538. ** Description:
  539. ** Determine at what point the current
  540. ** hint sequence is ending.
  541. ***/
  542. static short PickSequence(short left, short right, short pnt, TTStem *stems)
  543. {
  544. short left_end;
  545. short right_end;
  546. short new_seq;
  547. if (left!=UNDEF && right!=UNDEF) {
  548. left_end = EndOfRegion(pnt, &stems[left]);
  549. right_end = EndOfRegion(pnt, &stems[right]);
  550. new_seq = (short)MIN(left_end, right_end);
  551. } else if (left!=UNDEF) {
  552. left_end = EndOfRegion(pnt, &stems[left]);
  553. new_seq = left_end;
  554. } else {
  555. right_end = EndOfRegion(pnt, &stems[right]);
  556. new_seq = right_end;
  557. }
  558. return new_seq;
  559. }
  560. /***
  561. ** Function: CollectPoints
  562. **
  563. ** Description:
  564. ** This function will go through the points
  565. ** that are local extremas and interpolate
  566. ** them w.r.t. the enclosing stem sides.
  567. ** The non-extreme points are handled with
  568. ** an IUP[] instruction when this is done.
  569. ***/
  570. static short CollectPoints(const Outline *orgpaths,
  571. const short *ttpnts,
  572. TTStem *stems,
  573. short cnt,
  574. Extremas *extr,
  575. funit (*Position)(const Point))
  576. {
  577. const Outline *path;
  578. short xcnt = 0;
  579. short i,tot;
  580. short prev_stem;
  581. funit pos;
  582. short left, right;
  583. funit left_side, right_side;
  584. funit max, min;
  585. short max_pnt, min_pnt;
  586. short new_seq, n;
  587. short prev_pnt;
  588. funit prev_pos;
  589. short first;
  590. short pnt = UNDEF;
  591. tot = 0;
  592. for (path=orgpaths; path; path=path->next) {
  593. first = BoundingStems(tot,
  594. (short)(tot+(short)path->count),
  595. Position(path->pts[0]),
  596. stems, cnt, &left, &right);
  597. if (first==tot+(short)path->count) {
  598. tot = (short)(tot + path->count);
  599. continue;
  600. }
  601. new_seq = PickSequence(left, right, tot, stems);
  602. PickSides(left, right, &left_side, &right_side, stems);
  603. max = 1-SHRT_MAX/2;
  604. min_pnt = UNDEF;
  605. max_pnt = UNDEF;
  606. min = SHRT_MAX/2;
  607. prev_pnt = FALSE;
  608. prev_pos = UNDEF;
  609. prev_stem = UNDEF;
  610. for (i = (short)(first-tot); i<(short)path->count; i++) {
  611. if (OnCurve(path->onoff, i)) {
  612. pos = Position(path->pts[i]);
  613. n = (short)(i+tot);
  614. /* Have we crossed over a stem side. */
  615. if ((prev_stem!=RIGHTSTEM && pos<=left_side && max_pnt!=UNDEF) ||
  616. (prev_stem!=LEFTSTEM && pos>=right_side && min_pnt!=UNDEF)) {
  617. if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) {
  618. pnt = max_pnt;
  619. prev_pos = max;
  620. } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) {
  621. pnt = min_pnt;
  622. prev_pos = min;
  623. }
  624. xcnt = AddToBucket(extr, xcnt, ttpnts[pnt], left, right, stems);
  625. max = 1-SHRT_MAX/2;
  626. min = SHRT_MAX/2;
  627. max_pnt = UNDEF;
  628. min_pnt = UNDEF;
  629. prev_pnt = TRUE;
  630. }
  631. /* Crossing the side of a stem. */
  632. if ((pos>=right_side) || (pos<=left_side)) {
  633. if (pos<left_side)
  634. prev_stem = RIGHTSTEM;
  635. else
  636. prev_stem = LEFTSTEM;
  637. }
  638. /* Change left/right stem sides? */
  639. if ((n>new_seq) || (pos>=right_side) || (pos<=left_side)) {
  640. first = BoundingStems(n,
  641. (short)(path->count+tot),
  642. pos, stems, cnt,
  643. &left, &right);
  644. if (left==UNDEF && right==UNDEF)
  645. break;
  646. i = (short)(i + first - n);
  647. new_seq = PickSequence(left, right, n, stems);
  648. PickSides(left, right, &left_side, &right_side, stems);
  649. max = 1-SHRT_MAX/2;
  650. min = SHRT_MAX/2;
  651. max_pnt = UNDEF;
  652. min_pnt = UNDEF;
  653. }
  654. /* Is the point on the side of the stem? */
  655. if (CLOSETO(pos,left_side,2) || CLOSETO(pos,right_side,2)) {
  656. if (!prev_pnt || !CLOSETO(prev_pos, pos, 2)) {
  657. if (CLOSETO(pos, right_side, 2) ||
  658. CLOSETO(pos, left_side, 2)) {
  659. pnt = (short)n;
  660. prev_pos = pos;
  661. } else if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) {
  662. pnt = max_pnt;
  663. prev_pos = max;
  664. max_pnt = UNDEF;
  665. } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) {
  666. pnt = min_pnt;
  667. prev_pos = min;
  668. min_pnt = UNDEF;
  669. }
  670. xcnt = AddToBucket(extr, xcnt, ttpnts[pnt],
  671. left, right, stems);
  672. }
  673. prev_pnt = TRUE;
  674. prev_pos = pos;
  675. } else {
  676. prev_pnt = FALSE;
  677. /* New extremum candidate? */
  678. if (pos>max) {
  679. max = pos;
  680. max_pnt = (short)n;
  681. }
  682. if (pos<min) {
  683. min = pos;
  684. min_pnt = (short)n;
  685. }
  686. }
  687. }
  688. }
  689. if (left!=UNDEF || right!=UNDEF) {
  690. if (max_pnt!=UNDEF) {
  691. xcnt = AddToBucket(extr, xcnt, ttpnts[max_pnt],
  692. left, right, stems);
  693. }
  694. if (min_pnt!=UNDEF && min!=max) {
  695. xcnt = AddToBucket(extr, xcnt, ttpnts[min_pnt],
  696. left, right, stems);
  697. }
  698. }
  699. tot = (short)(tot + path->count);
  700. }
  701. return xcnt;
  702. }
  703. /***
  704. ** Function: CollectObliquePoints
  705. **
  706. ** Description:
  707. ** This function performs the same task as
  708. ** the "CollectPoint" function, with the
  709. ** exception that the outline is known to
  710. ** be sheared. Some of the logics
  711. ** is changed, bacause the IUP[] instruction
  712. ** and some IP instruction will not behave
  713. ** the same as in a non-sheared font.
  714. ** This differance applies only to vertical
  715. ** stems (hints resulting in horizontal motion of
  716. ** of points).
  717. ***/
  718. static short CollectObliquePoints(const Outline *orgpaths,
  719. const short *ttpnts,
  720. TTStem *stems,
  721. short cnt,
  722. Extremas *extr,
  723. funit (*Position)(const Point))
  724. {
  725. const Outline *path;
  726. short xcnt = 0;
  727. short i,tot;
  728. short prev_stem;
  729. funit pos;
  730. short left, right;
  731. funit left_side, right_side;
  732. funit max, min;
  733. short max_pnt, min_pnt;
  734. short new_seq, n;
  735. short first;
  736. short pnt = UNDEF;
  737. tot = 0;
  738. for (path=orgpaths; path; path=path->next) {
  739. first = BoundingStems(tot,
  740. (short)(tot+path->count),
  741. Position(path->pts[0]),
  742. stems, cnt, &left, &right);
  743. if (first==tot+(short)path->count) {
  744. tot = (short)(tot + path->count);
  745. continue;
  746. }
  747. new_seq = PickSequence(left, right, tot, stems);
  748. PickSides(left, right, &left_side, &right_side, stems);
  749. max = 1-SHRT_MAX/2;
  750. min_pnt = UNDEF;
  751. max_pnt = UNDEF;
  752. min = SHRT_MAX/2;
  753. prev_stem = UNDEF;
  754. for (i = (short)(first-tot); i<(short)path->count; i++) {
  755. if (OnCurve(path->onoff, i)) {
  756. pos = Position(path->pts[i]);
  757. n = (short)(i+tot);
  758. /* Have we crossed over a stem side. */
  759. if ((prev_stem!=RIGHTSTEM && pos<=left_side && max_pnt!=UNDEF) ||
  760. (prev_stem!=LEFTSTEM && pos>=right_side && min_pnt!=UNDEF)) {
  761. if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) {
  762. pnt = max_pnt;
  763. } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) {
  764. pnt = min_pnt;
  765. }
  766. max = 1-SHRT_MAX/2;
  767. min = SHRT_MAX/2;
  768. max_pnt = UNDEF;
  769. min_pnt = UNDEF;
  770. }
  771. /* Crossing the side of a stem. */
  772. if ((pos>=right_side) || (pos<=left_side)) {
  773. if (pos<left_side)
  774. prev_stem = RIGHTSTEM;
  775. else
  776. prev_stem = LEFTSTEM;
  777. }
  778. /* Change left/right stem sides? */
  779. if ((n>new_seq) || (pos>=right_side) || (pos<=left_side)) {
  780. first = BoundingStems(n,
  781. (short)(path->count+tot),
  782. pos, stems, cnt,
  783. &left, &right);
  784. if (left==UNDEF && right==UNDEF)
  785. break;
  786. i = (short)(i + first - n);
  787. new_seq = PickSequence(left, right, n, stems);
  788. PickSides(left, right, &left_side, &right_side, stems);
  789. max = 1-SHRT_MAX/2;
  790. min = SHRT_MAX/2;
  791. max_pnt = UNDEF;
  792. min_pnt = UNDEF;
  793. }
  794. /* Is the point on the side of the stem? */
  795. if (CLOSETO(pos,left_side,2) || CLOSETO(pos,right_side,2)) {
  796. if (CLOSETO(pos, right_side, 2)) {
  797. pnt = (short)n;
  798. if (stems[right].side1==right_side)
  799. xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt],
  800. stems[right].rp1);
  801. else
  802. xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt],
  803. stems[right].rp2);
  804. } else if (CLOSETO(pos, left_side, 2)) {
  805. pnt = (short)n;
  806. if (stems[left].side1==left_side)
  807. xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt],
  808. stems[left].rp1);
  809. else
  810. xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt],
  811. stems[left].rp2);
  812. } else if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) {
  813. pnt = max_pnt;
  814. max_pnt = UNDEF;
  815. } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) {
  816. pnt = min_pnt;
  817. min_pnt = UNDEF;
  818. }
  819. } else {
  820. /* New extremum candidate? */
  821. if (pos>max) {
  822. max = pos;
  823. max_pnt = (short)n;
  824. }
  825. if (pos<min) {
  826. min = pos;
  827. min_pnt = (short)n;
  828. }
  829. }
  830. }
  831. }
  832. if (left!=UNDEF || right!=UNDEF) {
  833. if (max_pnt!=UNDEF) {
  834. }
  835. if (min_pnt!=UNDEF && min!=max) {
  836. }
  837. }
  838. tot = (short)(tot + path->count);
  839. }
  840. return xcnt;
  841. }
  842. /***
  843. ** Function: AddRange
  844. **
  845. ** Description:
  846. ** This function adds a point range to
  847. ** a stem bucket.
  848. ***/
  849. static void AddRange(TTStem *stem, const short i1, const short i2)
  850. {
  851. short i;
  852. /* Check if a prior range can be extended. */
  853. if (i2!=ENDOFPATH) {
  854. for (i=0; i<stem->cnt; i++) {
  855. if (stem->range[i].from == i2+1)
  856. break;
  857. }
  858. } else {
  859. i = stem->cnt;
  860. }
  861. if (i==stem->cnt) {
  862. if (stem->cnt<MAXRANGE) {
  863. stem->range[stem->cnt].from = i1;
  864. stem->range[stem->cnt].to = i2;
  865. stem->cnt++;
  866. } else {
  867. LogError(MSG_WARNING, MSG_REPLC, NULL);
  868. }
  869. } else {
  870. stem->range[i].from = i1;
  871. }
  872. }
  873. /***
  874. ** Function: CreateStemBuckets
  875. **
  876. ** Description:
  877. ** This function will create stem buckets.
  878. ** Several duplicated T1 stem instructions
  879. ** may be mapped to the same bucket.
  880. ***/
  881. static short CreateStemBuckets(Stem *stemRoot,
  882. Stem3 *stem3Root,
  883. TTStem **result)
  884. {
  885. Stem3 *stem3, *stm3;
  886. Stem *stem, *stm;
  887. TTStem *stems = NULL;
  888. short i, j;
  889. short cnt;
  890. short tzpnt = TMPPNT1+1;
  891. /* Count the stems. */
  892. cnt = 0;
  893. (*result) = NULL;
  894. for (stem3=stem3Root; stem3; stem3=stem3->next) {
  895. /* Skip obsolete stems. */
  896. if (stem3->stem1.i2 == NORANGE)
  897. continue;
  898. /* Look for a duplicate. */
  899. for (stm3=stem3Root; stm3!=stem3; stm3=stm3->next) {
  900. if (stm3->stem1.offset==stem3->stem1.offset &&
  901. stm3->stem2.offset==stem3->stem2.offset &&
  902. stm3->stem3.offset==stem3->stem3.offset)
  903. break;
  904. }
  905. /* Count this stem if it is not a duplicate. */
  906. if (stm3==stem3)
  907. cnt = (short)(cnt + 3);
  908. }
  909. for (stem=stemRoot; stem; stem=stem->next) {
  910. /* Skip obsolete stems. */
  911. if (stem->i2 == NORANGE)
  912. continue;
  913. /* Look for a duplicate. */
  914. for (stm=stemRoot; stm!=stem; stm=stm->next) {
  915. if (stm->offset==stem->offset && stm->width==stem->width)
  916. break;
  917. }
  918. /* Don't count this stem if it is a duplicate. */
  919. if (stm==stem)
  920. cnt++;
  921. }
  922. /* Initiate them. */
  923. if (cnt) {
  924. if ((stems = Malloc(sizeof(TTStem)*(USHORT)cnt))==NULL) {
  925. errcode status;
  926. SetError(status=NOMEM);
  927. return status;
  928. }
  929. i = (short)(cnt-1);
  930. /* Initiate the buckets for the stem3s */
  931. for (stem3=stem3Root; stem3; stem3=stem3->next) {
  932. /* Skip obsolete stems. */
  933. if (stem3->stem1.i2 == NORANGE)
  934. continue;
  935. /* Skip if bucket exist for this stem already. */
  936. for (j=(short)(i+1); j<cnt; j++) {
  937. if (stems[j].side1==stem3->stem1.offset &&
  938. stems[j].side2==(stem3->stem1.offset+stem3->stem1.width))
  939. break;
  940. }
  941. if (j==cnt) {
  942. /* The rightmost stem is positioned w.r.t. to the middle. */
  943. stems[i].side1 = stem3->stem1.offset;
  944. stems[i].side2 = stem3->stem1.width + stem3->stem1.offset;
  945. stems[i].align = at_relative2;
  946. stems[i].ref = (short)(i-2);
  947. stems[i].rp1 = tzpnt++;
  948. stems[i].rp2 = tzpnt++;
  949. stems[i].cnt = 1;
  950. stems[i].range[0].from = stem3->stem1.i1;
  951. stems[i].range[0].to = stem3->stem1.i2;
  952. tzpnt+=2;
  953. i--;
  954. /* The leftmost stem is positioned w.r.t. to the middle. */
  955. stems[i].side1 = stem3->stem3.offset;
  956. stems[i].side2 = stem3->stem3.width + stem3->stem3.offset;
  957. stems[i].align = at_relative1;
  958. stems[i].ref = (short)(i-1);
  959. stems[i].rp1 = tzpnt++;
  960. stems[i].rp2 = tzpnt++;
  961. stems[i].cnt = 1;
  962. stems[i].range[0].from = stem3->stem1.i1;
  963. stems[i].range[0].to = stem3->stem1.i2;
  964. tzpnt+=2;
  965. i--;
  966. /* The middle stem is centered. */
  967. stems[i].side1 = stem3->stem2.offset;
  968. stems[i].side2 = stem3->stem2.width + stem3->stem2.offset;
  969. stems[i].align = at_centered;
  970. stems[i].rp1 = tzpnt++;
  971. stems[i].rp2 = tzpnt++;
  972. stems[i].cnt = 1;
  973. stems[i].range[0].from = stem3->stem1.i1;
  974. stems[i].range[0].to = stem3->stem1.i2;
  975. tzpnt+=2;
  976. i--;
  977. } else {
  978. AddRange(&stems[j-0], stem3->stem1.i1, stem3->stem1.i2);
  979. AddRange(&stems[j-1], stem3->stem3.i1, stem3->stem3.i2);
  980. AddRange(&stems[j-2], stem3->stem2.i1, stem3->stem2.i2);
  981. }
  982. }
  983. /* Initiate the buckets for the stems. */
  984. for (stem=stemRoot; stem; stem=stem->next) {
  985. /* Skip obsolete stems. */
  986. if (stem->i2 == NORANGE)
  987. continue;
  988. /* Skip if bucket exist for this stem already. */
  989. for (j=(short)(i+1); j<(short)cnt; j++) {
  990. if (stems[j].side1==stem->offset &&
  991. stems[j].side2==(stem->offset+stem->width))
  992. break;
  993. }
  994. /* Initiate new bucket:
  995. Plain vstems and hstems are centered by default. Some
  996. hstems may be top- or bottom-aligen at a latter point.
  997. Some stems may be positioned w.r.t. another vstem if
  998. they overlapp and the RELATIVESTEMS compiler flag is
  999. turned on. */
  1000. if (j==cnt) {
  1001. stems[i].side1 = stem->offset;
  1002. stems[i].side2 = stem->width + stem->offset;
  1003. stems[i].align = at_centered;
  1004. stems[i].rp1 = tzpnt++;
  1005. stems[i].rp2 = tzpnt++;
  1006. stems[i].cnt = 1;
  1007. stems[i].range[0].from = stem->i1;
  1008. stems[i].range[0].to = stem->i2;
  1009. tzpnt+=2;
  1010. i--;
  1011. } else {
  1012. AddRange(&stems[j], stem->i1, stem->i2);
  1013. }
  1014. }
  1015. /* This happens if two stems are defined for the same
  1016. hint replacement region and the same position, which
  1017. is an Adobe Type 1 font error (broken font). The
  1018. converter will recover by ignoring redundant stems. */
  1019. if (i!=-1) {
  1020. /* LogError(MSG_STEM3); */
  1021. for (j=0; j<=i; j++) {
  1022. stems[j].cnt = 0;
  1023. }
  1024. }
  1025. }
  1026. (*result) = stems;
  1027. return (short)cnt;
  1028. }
  1029. /***
  1030. ** Function: ResolveRelativeStem
  1031. **
  1032. ** Description:
  1033. ** This function decides if two stems should
  1034. ** be aligned side1->side1, side2->side2,
  1035. ** side1->side2 or side2->side1.
  1036. ** Stem are positition in relation to each
  1037. ** other for two reasons: They overlapp, they
  1038. ** are aligned side by side or they are
  1039. ** members of a stem3 hint.
  1040. ***/
  1041. static void ResolveRelativeStem(TTStem *ref, TTStem *cur)
  1042. {
  1043. /* SIDE1->SIDE2 */
  1044. if (cur->side1==ref->side2) {
  1045. cur->ref = ref->rp2;
  1046. cur->align = at_relative1;
  1047. /* SIDE1->SIDE2 */
  1048. } else if (cur->side2==ref->side1) {
  1049. cur->ref = ref->rp1;
  1050. cur->align = at_relative2;
  1051. /* SIDE1->SIDE1 */
  1052. } else if ((cur->side1>ref->side1) &&
  1053. ((cur->side1-ref->side1+10)>=
  1054. (cur->side2-ref->side2))) {
  1055. cur->ref = ref->rp1;
  1056. cur->align = at_relative1;
  1057. /* SIDE2->SIDE2 */
  1058. } else {
  1059. cur->ref = ref->rp2;
  1060. cur->align = at_relative2;
  1061. }
  1062. }
  1063. /***
  1064. ** Function: ConvertVStems
  1065. **
  1066. ** Description:
  1067. ** This function translate vstem and vstem3 to TT instructions.
  1068. ***/
  1069. static errcode ConvertVStems(struct T1Metrics *t1m,
  1070. const Hints *hints,
  1071. const Outline *orgpaths,
  1072. const short *ttpnts,
  1073. UBYTE *pgm,
  1074. short *pc_ptr,
  1075. short *args,
  1076. short *pcd_ptr,
  1077. USHORT *twilight_ptr)
  1078. {
  1079. Extremas extr[MAXEXTR];
  1080. short xcnt = 0;
  1081. errcode status = SUCCESS;
  1082. short pc = *pc_ptr;
  1083. short pcd = *pcd_ptr;
  1084. TTStem *stems = NULL;
  1085. short i;
  1086. short cnt;
  1087. /* Create the buckets. */
  1088. if ((cnt = CreateStemBuckets(hints->vstems,
  1089. hints->vstems3,
  1090. &(stems)))==NOMEM) {
  1091. status = NOMEM;
  1092. } else {
  1093. /* Update Max num of twilight points. */
  1094. if ((cnt*TWILIGHTS_PER_STEM+TMP_TWILIGHTS) > (long)(*twilight_ptr))
  1095. (*twilight_ptr) = (USHORT)(cnt * TWILIGHTS_PER_STEM + TMP_TWILIGHTS);
  1096. if (cnt && stems) {
  1097. #if RELATIVESTEMS
  1098. /* Do counter- and overlappning stem control? */
  1099. for (i=0; i<cnt; i++) {
  1100. short j;
  1101. if (stems[i].align==at_centered) {
  1102. funit prox = (funit)(ABS(MAX(100,
  1103. stems[i].side2 -
  1104. stems[i].side1)));
  1105. funit prox2;
  1106. prox2 = (funit)(prox/2);
  1107. for (j=0; j<i; j++) {
  1108. if (stems[j].cnt &&
  1109. !((stems[i].side1 - (funit)prox > stems[j].side2) ||
  1110. (stems[i].side2 + (funit)prox < stems[j].side1)) &&
  1111. (ABS(stems[i].side2-stems[i].side1-
  1112. (stems[j].side2-stems[j].side1)) < prox2 ||
  1113. (short)(stems[i].side1 > stems[j].side2) !=
  1114. (short)(stems[i].side2 < stems[j].side1)))
  1115. break;
  1116. }
  1117. if (i!=j) {
  1118. if (stems[j].side1 < stems[i].side1)
  1119. stems[i].align = at_relative1;
  1120. else
  1121. stems[i].align = at_relative2;
  1122. stems[i].ref = j;
  1123. }
  1124. }
  1125. }
  1126. #endif
  1127. /** Vertical stem hints */
  1128. EmitVerticalStems(pgm, &pc, args, &pcd);
  1129. /* Handle sheared fonts by settin the projection
  1130. vector to the italic angle. The TT instructions for
  1131. the T1 hints can handle any projection vector. */
  1132. if (t1m->fmatrix!=DEFAULTMATRIX && GetFontMatrix(t1m)[2]!=0) {
  1133. Point pt;
  1134. pt.x = 0; pt.y = 1000;
  1135. TransAllPoints(t1m, &pt, (short)1, GetFontMatrix(t1m));
  1136. SetProjection(pgm, &pc, args, &pcd, pt.x, pt.y);
  1137. }
  1138. /* Convert the buckets into instructions. */
  1139. for (i=0; i<cnt; i++) {
  1140. if (stems[i].cnt==0)
  1141. continue;
  1142. /* Resolve relative stems */
  1143. if ((stems[i].align == at_relative1 ||
  1144. stems[i].align == at_relative2) &&
  1145. stems[i].ref != UNDEF)
  1146. ResolveRelativeStem(&stems[stems[i].ref], &stems[i]);
  1147. /* Emit the instructions. */
  1148. status = EmitVStem(pgm, &pc, args, &pcd, t1m,
  1149. ABS(stems[i].side2 - stems[i].side1),
  1150. TransRX(t1m, stems[i].side1),
  1151. TransRX(t1m, stems[i].side2),
  1152. Trans3X(t1m, stems[i].side1),
  1153. Trans3X(t1m, stems[i].side2),
  1154. (short)MIN(stems[i].rp1, stems[i].rp2),
  1155. stems[i].align,
  1156. stems[i].ref);
  1157. if (status!=SUCCESS)
  1158. break;
  1159. }
  1160. /* Collect extremas residing within and between stem sides. */
  1161. if (SyntheticOblique(t1m)) {
  1162. xcnt = CollectObliquePoints(orgpaths, ttpnts,
  1163. stems, cnt, extr, PosX);
  1164. } else {
  1165. xcnt = CollectPoints(orgpaths, ttpnts, stems, cnt,
  1166. extr, PosX);
  1167. }
  1168. /* Do the 3% scaling */
  1169. ScaleDown3(extr, xcnt, pgm, &pc, args, &pcd);
  1170. /* Switch over to GLYPHZONE */
  1171. pgm[pc++] = op_szp2;
  1172. args[pcd++] = 1;
  1173. /* Interpolate the local extremas. */
  1174. EmitIP(extr, xcnt, pgm, &pc, args, &pcd, (short)SECONDPAIR);
  1175. /* Interpolate/Shift the rest. */
  1176. pgm[pc++] = op_iup | SUBOP_X;
  1177. /* Free used resources */
  1178. if (stems)
  1179. Free(stems);
  1180. }
  1181. }
  1182. *pc_ptr = pc;
  1183. *pcd_ptr = pcd;
  1184. return status;
  1185. }
  1186. /***
  1187. ** Function: ResolveBlueHStem3
  1188. **
  1189. ** Description:
  1190. ** This function attemts to resolves a conflict between
  1191. ** a hstem3 that has one of its stems in an alignment zone,
  1192. ** if there is such a conflict.
  1193. ***/
  1194. static short ResolveBlueHStem3(TTStem *stems,
  1195. const short cnt,
  1196. const short k)
  1197. {
  1198. short ref = stems[k].ref;
  1199. TTStem tmp;
  1200. short i;
  1201. /* The parent stem of a hstem3 must be first in the 'stems' array,
  1202. i.e. the order of the stems is important. The children stems may
  1203. therefore have to be swaped with the parten to enforce this condition. */
  1204. if ((stems[k].align==at_relative1 ||
  1205. stems[k].align==at_relative2) &&
  1206. (stems[ref].align!=at_relative1 &&
  1207. stems[ref].align!=at_relative2 &&
  1208. stems[ref].align!=at_side1 &&
  1209. stems[ref].align!=at_side2)) {
  1210. tmp = stems[k];
  1211. stems[k] = stems[ref];
  1212. stems[k].align = at_relative1;
  1213. stems[k].ref = ref;
  1214. stems[ref] = tmp;
  1215. for (i=0; i<cnt; i++) {
  1216. if (i!=k && i!=ref &&
  1217. (stems[i].align==at_relative1 ||
  1218. stems[i].align==at_relative2) &&
  1219. stems[i].ref == ref) {
  1220. stems[i].ref = (short)k;
  1221. if (i<k) {
  1222. tmp = stems[k];
  1223. stems[k] = stems[i];
  1224. stems[i] = tmp;
  1225. }
  1226. break;
  1227. }
  1228. }
  1229. } else {
  1230. ref = k;
  1231. }
  1232. return ref;
  1233. }
  1234. /***
  1235. ** Function: ConvertHStems
  1236. **
  1237. ** Description:
  1238. ** This function converts hstem and hstem3 T1 instructions.
  1239. ***/
  1240. static errcode ConvertHStems(struct T1Metrics *t1m,
  1241. const Hints *hints,
  1242. const Outline *orgpaths,
  1243. const short *ttpnts,
  1244. UBYTE *pgm,
  1245. short *pc_ptr,
  1246. short *args,
  1247. short *pcd_ptr,
  1248. USHORT *twilight_ptr)
  1249. {
  1250. Extremas extr[MAXEXTR];
  1251. short xcnt = 0;
  1252. errcode status = SUCCESS;
  1253. short pc = *pc_ptr;
  1254. short pcd = *pcd_ptr;
  1255. TTStem *stems = NULL;
  1256. short i, k;
  1257. short cnt;
  1258. short cvt;
  1259. /* Create the stem buckets. */
  1260. cnt = CreateStemBuckets(hints->hstems, hints->hstems3, &(stems));
  1261. if (cnt==NOMEM)
  1262. return NOMEM;
  1263. /* Update Max num of twilight points. */
  1264. if ((USHORT)(cnt*TWILIGHTS_PER_STEM+TMP_TWILIGHTS) > (*twilight_ptr))
  1265. (*twilight_ptr) = (USHORT)(cnt * TWILIGHTS_PER_STEM + TMP_TWILIGHTS);
  1266. #if RELATIVESTEMS
  1267. /* Do counter- and overlappning stem control? */
  1268. for (i=0; i<cnt; i++) {
  1269. short j;
  1270. if (stems[i].align==at_centered) {
  1271. funit prox = (funit)(ABS(MAX(100, stems[i].side2 - stems[i].side1)));
  1272. funit prox2;
  1273. prox2 = (funit)(prox/2);
  1274. for (j=0; j<i; j++) {
  1275. if (stems[j].cnt &&
  1276. !((stems[i].side1 - (funit)prox > stems[j].side2) ||
  1277. (stems[i].side2 + (funit)prox < stems[j].side1)) &&
  1278. (ABS(stems[i].side2-stems[i].side1-
  1279. (stems[j].side2-stems[j].side1)) < prox2 ||
  1280. (short)(stems[i].side1 > stems[j].side2) !=
  1281. (short)(stems[i].side2 < stems[j].side1)))
  1282. break;
  1283. }
  1284. if (i!=j) {
  1285. if (stems[j].side1 < stems[i].side1)
  1286. stems[i].align = at_relative1;
  1287. else
  1288. stems[i].align = at_relative2;
  1289. stems[i].ref = j;
  1290. }
  1291. }
  1292. }
  1293. #endif
  1294. /* Do alignment control. */
  1295. for (i=0; i<cnt; i++) {
  1296. if ((cvt=GetBottomPos(GetBlues(t1m),
  1297. GetAlignment(t1m),
  1298. stems[i].side1))!=UNDEF) {
  1299. k = ResolveBlueHStem3(stems, cnt, i);
  1300. stems[k].ref = cvt;
  1301. stems[k].align = at_side1;
  1302. } else if ((cvt=GetTopPos(GetBlues(t1m),
  1303. GetAlignment(t1m),
  1304. stems[i].side2))!=UNDEF) {
  1305. k = ResolveBlueHStem3(stems, cnt, i);
  1306. stems[k].ref = cvt;
  1307. stems[k].align = at_side2;
  1308. }
  1309. }
  1310. if (cnt && stems) {
  1311. /** Horizontal stem hints */
  1312. EmitHorizontalStems(pgm, &pc, args, &pcd);
  1313. /* Convert the buckets into instructions. */
  1314. for (i=0; i<cnt; i++) {
  1315. if (stems[i].cnt==0)
  1316. continue;
  1317. /* Resolve relative stems */
  1318. if ((stems[i].align == at_relative1 ||
  1319. stems[i].align == at_relative2) &&
  1320. stems[i].ref != UNDEF)
  1321. ResolveRelativeStem(&stems[stems[i].ref], &stems[i]);
  1322. /* Emit the instructions. */
  1323. status = EmitHStem(pgm, &pc, args, &pcd, t1m,
  1324. stems[i].side2 - stems[i].side1,
  1325. TransY(t1m, stems[i].side1),
  1326. TransY(t1m, stems[i].side2),
  1327. (short)MIN(stems[i].rp1, stems[i].rp2),
  1328. stems[i].align,
  1329. stems[i].ref);
  1330. if (status!=SUCCESS)
  1331. break;
  1332. }
  1333. /* Interpolate extremas residing within and between stem sides. */
  1334. xcnt = CollectPoints(orgpaths, ttpnts, stems, cnt, extr, PosY);
  1335. /* Switch over to GLYPHZONE */
  1336. pgm[pc++] = op_szp2;
  1337. args[pcd++] = 1;
  1338. /* Interpolate the local extremas. */
  1339. EmitIP(extr, xcnt, pgm, &pc, args, &pcd, (short)0);
  1340. /* Interpoalte/Shift the rest. */
  1341. pgm[pc++] = op_iup | SUBOP_Y;
  1342. /* Free used resources */
  1343. if (stems)
  1344. Free(stems);
  1345. }
  1346. *pcd_ptr = pcd;
  1347. *pc_ptr = pc;
  1348. return status;
  1349. }
  1350. /***** FUNCTIONS */
  1351. /***
  1352. ** Function: GetRomanHints
  1353. **
  1354. ** Description:
  1355. ***/
  1356. const UBYTE *GetRomanHints(int *size)
  1357. {
  1358. (*size) = sizeof(roman_hints);
  1359. return roman_hints;
  1360. }
  1361. /***
  1362. ** Function: GetSwissHints
  1363. **
  1364. ** Description:
  1365. ***/
  1366. const UBYTE *GetSwissHints(int *size)
  1367. {
  1368. (*size) = sizeof(swiss_hints);
  1369. return swiss_hints;
  1370. }
  1371. /***
  1372. ** Function: MatchingFamily
  1373. **
  1374. ** Description:
  1375. ** Locate the family alignment zone that is closest to
  1376. ** a given alignment zone.
  1377. ***/
  1378. short MatchingFamily(const funit pos,
  1379. const funit *family,
  1380. const USHORT fcnt)
  1381. {
  1382. funit min_dist = SHRT_MAX;
  1383. short k = UNDEF;
  1384. USHORT j;
  1385. /* Look for the closest family blue. */
  1386. for (j=0; j<fcnt; j+=2) {
  1387. if (ABS(family[j] - pos) < min_dist) {
  1388. k = (short)j;
  1389. min_dist = ABS(family[j] - pos);
  1390. }
  1391. }
  1392. return k;
  1393. }
  1394. /***
  1395. ** Function: ConvertHints
  1396. **
  1397. ** Description:
  1398. ** This functions converts hstem, hstem3, vstem, vstem3 and flex
  1399. ** hints, as well as doing diagonal control.
  1400. ***/
  1401. errcode ConvertHints(struct T1Metrics *t1m,
  1402. const Hints *hints,
  1403. const Outline *orgpaths,
  1404. const Outline *paths,
  1405. const short *ttpnts,
  1406. UBYTE **gpgm,
  1407. USHORT *num,
  1408. USHORT *stack,
  1409. USHORT *twilight)
  1410. {
  1411. errcode status = SUCCESS;
  1412. UBYTE *pgm = NULL;
  1413. short *args = NULL;
  1414. short pc = 0;
  1415. short pcd = 0;
  1416. short cnt = 0;
  1417. short narg = 0;
  1418. short marg = 0;
  1419. /* Access resources. */
  1420. pgm=GetCodeStack(t1m);
  1421. args=GetArgStack(t1m);
  1422. /* Convert the vertical stem hints. */
  1423. if (status==SUCCESS)
  1424. status = ConvertVStems(t1m, hints, orgpaths, ttpnts,
  1425. pgm, &pc, args, &pcd, twilight);
  1426. /* Convert the horizontal stem hints. */
  1427. if (status==SUCCESS)
  1428. status = ConvertHStems(t1m, hints, orgpaths, ttpnts,
  1429. pgm, &pc, args, &pcd, twilight);
  1430. /* Convert flex hints. */
  1431. if (status==SUCCESS)
  1432. status = ConvertFlex(t1m, hints->flex, ttpnts,
  1433. pgm, &pc, args, &pcd, &marg);
  1434. /********************
  1435. * Adjust diagonals
  1436. * Do not reduce if dominant vertical stem width is more than
  1437. * 2.0 pels at 11PPEm and above. This occurs when:
  1438. * 1) StdVW > 187
  1439. * 2) StdVW < 100 and ForceBold = TRUE
  1440. **/
  1441. if ((ForceBold(t1m)==1 && GetStdVW(t1m)>100 && GetStdVW(t1m)<187) ||
  1442. (ForceBold(t1m)==0 && GetStdVW(t1m)<187))
  1443. narg = ReduceDiagonals(paths, pgm, &pc, args, &pcd);
  1444. if (narg>marg)
  1445. marg = narg;
  1446. if (pc>PGMSIZE) {
  1447. SetError(status = TTSTACK);
  1448. }
  1449. if (pcd>ARGSIZE) {
  1450. SetError(status = ARGSTACK);
  1451. }
  1452. /* Allocate the gpgm */
  1453. (*gpgm) = NULL;
  1454. (*num) = 0;
  1455. (*stack) = 0;
  1456. if (status==SUCCESS) {
  1457. if (pc) {
  1458. if (((*gpgm) = Malloc((USHORT)(pc+pcd*3)))==NULL) {
  1459. SetError(status = NOMEM);
  1460. } else {
  1461. /* Assemble the arguments for the instructions */
  1462. cnt = 0;
  1463. AssembleArgs(args, pcd, (*gpgm), &cnt);
  1464. memcpy(&(*gpgm)[cnt], pgm, (USHORT)pc);
  1465. (*num) = (USHORT)(cnt + pc);
  1466. (*stack) = (USHORT)(pcd + marg);
  1467. }
  1468. }
  1469. }
  1470. return status;
  1471. }
  1472. /***
  1473. ** Function: BuildPreProgram
  1474. **
  1475. ** Description:
  1476. ** This function builds the pre-program that will compute
  1477. ** the CVT and storage entries for the TT stem hint
  1478. ** instructions to work.
  1479. ***/
  1480. USHORT BuildPreProgram(const struct T1Metrics *t1m,
  1481. const WeightControl *weight,
  1482. Blues *blues,
  1483. AlignmentControl *align,
  1484. UBYTE **glob_prep,
  1485. const int prepsize,
  1486. USHORT *maxstack)
  1487. {
  1488. UBYTE *prep = (*glob_prep);
  1489. short *args = NULL;
  1490. short ta, tp = 0;
  1491. USHORT i, j;
  1492. long shift;
  1493. funit stdvw, stdhw;
  1494. short cis;
  1495. funit std_width;
  1496. USHORT std_tres;
  1497. funit min_dist;
  1498. short k;
  1499. short argsize = ARGSIZE;
  1500. short psize = (short)prepsize;
  1501. /* Allocate work space. */
  1502. if ((args=Malloc(sizeof(args[0])*(USHORT)argsize))==NULL) {
  1503. LogError(MSG_ERROR, MSG_NOMEM, NULL);
  1504. } else {
  1505. /* Copy the standard pre-program. */
  1506. memcpy(prep, PrepProg, sizeof(PrepProg));
  1507. tp = sizeof(PrepProg);
  1508. (*maxstack) = 0;
  1509. /**********
  1510. * Compute Blue values.
  1511. */
  1512. prep[tp++] = op_pushb1; prep[tp++] = blues->blueScale;
  1513. prep[tp++] = op_mppem;
  1514. prep[tp++] = op_lt;
  1515. prep[tp++] = op_if;
  1516. prep[tp++] = op_pushb1;
  1517. prep[tp++] = ONEPIXEL;
  1518. prep[tp++] = op_smd;
  1519. prep[tp++] = op_pushb1;
  1520. prep[tp++] = TWILIGHT;
  1521. prep[tp++] = op_szps;
  1522. prep[tp++] = op_svcta | SUBOP_Y;
  1523. prep[tp++] = op_rtg;
  1524. /***********************/
  1525. /*** ABOVE BlueScale ***/
  1526. /***********************/
  1527. /* Align the top zones. */
  1528. for (i=0; i<blues->blue_cnt/2; i++) {
  1529. min_dist = SHRT_MAX;
  1530. k = UNDEF;
  1531. /*** Copy the FamilyBlue entries to the BlueValues if */
  1532. /*** below the Family cut in size. */
  1533. if (blues->fblue_cnt>0) {
  1534. /* Do the cut in on FamilyBlue/BlueValue. */
  1535. k = MatchingFamily(blues->bluevalues[i*2],
  1536. blues->familyblues,
  1537. blues->fblue_cnt);
  1538. min_dist = ABS(blues->bluevalues[i*2] - blues->familyblues[k]);
  1539. /* Always FamilyBlue? */
  1540. if (min_dist) {
  1541. cis = (short)(GetUPEM(t1m) / TransY(t1m, min_dist));
  1542. tp = (short)FamilyCutIn(prep, (USHORT)tp, cis);
  1543. }
  1544. /* Allocate a cvt if this family has not been used before. */
  1545. if (blues->family_cvt[k/2]==UNDEF_CVT) {
  1546. blues->family_cvt[k/2] = align->cvt;
  1547. align->cvt += 2;
  1548. }
  1549. ta = 2;
  1550. CHECK_ARGSIZE(args, ta, align->top[i].cnt, argsize);
  1551. for (j=0; j<align->top[i].cnt; j++) {
  1552. args[ta++] = (short)align->top[i].pos[j].cvt;
  1553. }
  1554. CHECK_PREPSIZE(prep, tp, 2*ta+10, psize);
  1555. tp = (short)CopyFamilyBlue(prep, tp, args, ta);
  1556. if ((ta+2)>(int)(*maxstack))
  1557. (*maxstack) = (USHORT)(ta+2);
  1558. /* Set up the zone. */
  1559. tp = (short)SetZone(prep, (USHORT)tp,
  1560. (short)(blues->family_cvt[k/2]));
  1561. if (min_dist>0)
  1562. prep[tp++] = op_else;
  1563. }
  1564. /*** Set up the zone. */
  1565. CHECK_PREPSIZE(prep, tp, STACKINC, psize);
  1566. tp = (short)SetZone(prep, (USHORT)tp,
  1567. (short)(align->top[i].blue_cvt));
  1568. if (k!=UNDEF && min_dist) {
  1569. prep[tp++] = op_eif;
  1570. }
  1571. /*** Round and enforce overshoot. */
  1572. ta = 2;
  1573. CHECK_ARGSIZE(args, ta, align->top[i].cnt, argsize);
  1574. for (j=0; j<align->top[i].cnt; j++) {
  1575. if ((align->top[i].pos[j].y -
  1576. blues->bluevalues[i*2])*F8D8 > blues->blueShift) {
  1577. args[ta++] = (short)align->top[i].pos[j].cvt;
  1578. }
  1579. }
  1580. if (ta>2) {
  1581. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1582. tp = (short)AlignOvershoot(prep, tp, args, ta);
  1583. if (ta>(short)(*maxstack))
  1584. (*maxstack) = (USHORT)ta;
  1585. }
  1586. ta = 2;
  1587. CHECK_ARGSIZE(args, ta, align->top[i].cnt, argsize);
  1588. for (j=0; j<align->top[i].cnt; j++) {
  1589. if ((align->top[i].pos[j].y -
  1590. blues->bluevalues[i*2])*F8D8 <= blues->blueShift) {
  1591. args[ta++] = (short)align->top[i].pos[j].cvt;
  1592. }
  1593. }
  1594. if (ta>2) {
  1595. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1596. tp = (short)AlignFlat(prep, tp, args, ta);
  1597. if (ta>(short)(*maxstack))
  1598. (*maxstack) = (USHORT)(ta+2);
  1599. }
  1600. }
  1601. /* Align the bottom zones. */
  1602. for (i=0; i<blues->oblue_cnt/2; i++) {
  1603. min_dist = SHRT_MAX;
  1604. k = UNDEF;
  1605. /*** Copy the FamilyBlue entries to the BlueValues if */
  1606. /*** below the Family cut in size. */
  1607. if (blues->foblue_cnt>0) {
  1608. /* Do the cut in on FamilyBlue/BlueValue. */
  1609. k = MatchingFamily(blues->otherblues[i*2],
  1610. blues->familyotherblues,
  1611. blues->foblue_cnt);
  1612. min_dist = ABS(blues->otherblues[i*2] -
  1613. blues->familyotherblues[k]);
  1614. /* Always FamilyBlue? */
  1615. if (min_dist) {
  1616. cis = (short)(GetUPEM(t1m) / TransY(t1m, min_dist));
  1617. tp = (short)FamilyCutIn(prep, (USHORT)tp, cis);
  1618. }
  1619. /* Allocate a cvt if this family has not been used before. */
  1620. if (blues->familyother_cvt[k/2]==UNDEF_CVT) {
  1621. blues->familyother_cvt[k/2] = align->cvt++;
  1622. }
  1623. ta = 2;
  1624. CHECK_ARGSIZE(args, ta, align->bottom[i].cnt, argsize);
  1625. for (j=0; j<align->bottom[i].cnt; j++) {
  1626. args[ta++] = (short)align->bottom[i].pos[j].cvt;
  1627. }
  1628. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1629. tp = (short)CopyFamilyBlue(prep, tp, args, ta);
  1630. if (ta>(short)(*maxstack))
  1631. (*maxstack) = (USHORT)ta;
  1632. /* Set up the zone. */
  1633. tp = (short)SetZone(prep, (USHORT)tp,
  1634. (short)blues->familyother_cvt[k/2]);
  1635. if (min_dist>0)
  1636. prep[tp++] = op_else;
  1637. }
  1638. /*** Set up the zone. */
  1639. tp = (short)SetZone(prep, (USHORT)tp,
  1640. (short)align->bottom[i].blue_cvt);
  1641. if (k!=UNDEF && min_dist) {
  1642. prep[tp++] = op_eif;
  1643. }
  1644. /*** Round and enforce overshoot. */
  1645. ta = 2;
  1646. CHECK_ARGSIZE(args, ta, align->bottom[i].cnt, argsize);
  1647. for (j=0; j<align->bottom[i].cnt; j++) {
  1648. if ((align->bottom[i].pos[j].y -
  1649. blues->otherblues[i*2+1])*F8D8 > blues->blueShift) {
  1650. args[ta++] = (short)align->bottom[i].pos[j].cvt;
  1651. }
  1652. }
  1653. if (ta>2) {
  1654. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1655. tp = (short)AlignOvershoot(prep, tp, args, ta);
  1656. if (ta>(short)(*maxstack))
  1657. (*maxstack) = (USHORT)ta;
  1658. }
  1659. ta = 2;
  1660. CHECK_ARGSIZE(args, ta, align->bottom[i].cnt, argsize);
  1661. for (j=0; j<align->bottom[i].cnt; j++) {
  1662. if ((align->bottom[i].pos[j].y -
  1663. blues->otherblues[i*2+1])*F8D8 <= blues->blueShift) {
  1664. args[ta++] = (short)align->bottom[i].pos[j].cvt;
  1665. }
  1666. }
  1667. if (ta>2) {
  1668. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1669. tp = (short)AlignFlat(prep, tp, args, ta);
  1670. if (ta>(short)(*maxstack))
  1671. (*maxstack) = (USHORT)(ta+2);
  1672. }
  1673. }
  1674. /***********************/
  1675. /*** BELOW BlueScale ***/
  1676. /***********************/
  1677. prep[tp++] = op_else;
  1678. /*** Align the top zones. */
  1679. for (i=0; i<blues->blue_cnt/2; i++) {
  1680. /* Initiate */
  1681. min_dist = SHRT_MAX;
  1682. k = UNDEF;
  1683. /* switch between blues and family blues. */
  1684. if (blues->fblue_cnt) {
  1685. /* Look for the closest family blue. */
  1686. k = MatchingFamily(blues->bluevalues[i*2],
  1687. blues->familyblues,
  1688. blues->fblue_cnt);
  1689. min_dist = ABS(blues->bluevalues[i*2] - blues->familyblues[k]);
  1690. /* Copy/Round the family overshoot position to the zone. */
  1691. if (min_dist) {
  1692. cis = (short)(GetUPEM(t1m) / TransY(t1m, (funit)min_dist));
  1693. tp = (short)FamilyCutIn(prep, (USHORT)tp, cis);
  1694. ta = 2;
  1695. CHECK_ARGSIZE(args, ta, align->top[i].cnt*2, argsize);
  1696. for (j=0; j<align->top[i].cnt; j++) {
  1697. args[ta++] = (short)(blues->family_cvt[k/2] + 1);
  1698. args[ta++] = (short)(align->top[i].pos[j].cvt);
  1699. }
  1700. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1701. tp = (short)CopyZone(prep, tp, args, ta);
  1702. if (ta>(short)(*maxstack))
  1703. (*maxstack) = (USHORT)(ta+2);
  1704. prep[tp++] = op_else;
  1705. }
  1706. }
  1707. /* Copy/Round the blue overshoot position to the zone position. */
  1708. ta = 2;
  1709. CHECK_ARGSIZE(args, ta, align->top[i].cnt*2, argsize);
  1710. for (j=0; j<align->top[i].cnt; j++) {
  1711. args[ta++] = (short)(align->top[i].blue_cvt + 1);
  1712. args[ta++] = (short)(align->top[i].pos[j].cvt);
  1713. }
  1714. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1715. tp = (short)CopyZone(prep, tp, args, ta);
  1716. if (ta>(short)(*maxstack))
  1717. (*maxstack) = (USHORT)ta;
  1718. if (k!=UNDEF && min_dist>0)
  1719. prep[tp++] = op_eif;
  1720. }
  1721. /*** Align the bottom zones. */
  1722. for (i=0; i<blues->oblue_cnt/2; i++) {
  1723. /* Initiate. */
  1724. min_dist = SHRT_MAX;
  1725. k = UNDEF;
  1726. /* switch between blues and family blues. */
  1727. if (blues->foblue_cnt) {
  1728. /* Look for the closest family blue. */
  1729. k = MatchingFamily(blues->otherblues[i*2],
  1730. blues->familyotherblues,
  1731. blues->foblue_cnt);
  1732. min_dist = ABS(blues->otherblues[i*2] -
  1733. blues->familyotherblues[k]);
  1734. /* Copy/Round the family overshoot position to the zone. */
  1735. if (min_dist) {
  1736. cis = (short)(GetUPEM(t1m) / TransY(t1m, (funit)min_dist));
  1737. tp = (short)FamilyCutIn(prep, (USHORT)tp, cis);
  1738. ta = 2;
  1739. CHECK_ARGSIZE(args, ta, align->bottom[i].cnt*2, argsize);
  1740. for (j=0; j<align->bottom[i].cnt; j++) {
  1741. args[ta++] = (short)(blues->familyother_cvt[k/2]);
  1742. args[ta++] = (short)(align->bottom[i].pos[j].cvt);
  1743. }
  1744. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1745. tp = (short)CopyZone(prep, tp, args, ta);
  1746. if (ta>(short)(*maxstack))
  1747. (*maxstack) = (USHORT)ta;
  1748. prep[tp++] = op_else;
  1749. }
  1750. }
  1751. /* Copy/Round the blue overshoot position to the zone position. */
  1752. ta = 2;
  1753. CHECK_ARGSIZE(args, ta, align->bottom[i].cnt*2, argsize);
  1754. for (j=0; j<align->bottom[i].cnt; j++) {
  1755. args[ta++] = (short)(align->bottom[i].blue_cvt);
  1756. args[ta++] = (short)(align->bottom[i].pos[j].cvt);
  1757. }
  1758. CHECK_PREPSIZE(prep, tp, ta*2+4, psize);
  1759. tp = (short)CopyZone(prep, tp, args, ta);
  1760. if (ta>(short)(*maxstack))
  1761. (*maxstack) = (USHORT)ta;
  1762. if (k!=UNDEF && min_dist>0)
  1763. prep[tp++] = op_eif;
  1764. }
  1765. /* EIF[] MMPEM<BlueScale */
  1766. prep[tp++] = op_eif;
  1767. prep[tp++] = op_pushb1;
  1768. prep[tp++] = 64;
  1769. prep[tp++] = op_smd;
  1770. /**************************************/
  1771. /*** STEM WEIGHT CONTROL ***/
  1772. /**************************************/
  1773. /****** ForceBold ***/
  1774. if (ForceBold(t1m)) {
  1775. prep[tp++] = op_pushb1+2;
  1776. prep[tp++] = STDV_CVT;
  1777. prep[tp++] = ONEPIXEL*3/4;
  1778. prep[tp++] = STDV_CVT;
  1779. prep[tp++] = op_rcvt;
  1780. prep[tp++] = op_max;
  1781. prep[tp++] = op_wcvtp;
  1782. }
  1783. /******
  1784. * Compute width of horizontal stems.
  1785. */
  1786. prep[tp++] = op_rtdg;
  1787. prep[tp++] = op_svcta | SUBOP_Y;
  1788. if ((std_width = GetStdHW(t1m))==0)
  1789. std_width = GetDefStdHW(t1m);
  1790. std_width = TransY(t1m, std_width);
  1791. std_tres = (USHORT)(GetUPEM(t1m) * 2 / std_width);
  1792. ta = 0;
  1793. CHECK_ARGSIZE(args, ta, STEMSNAPARGS*weight->cnt_hw, argsize);
  1794. for (i=0; i<weight->cnt_hw; i++) {
  1795. funit width = TransY(t1m, weight->hwidths[i].width);
  1796. short snap = GetSnapH(t1m, weight->hwidths[i].width);
  1797. USHORT storage = weight->hwidths[i].storage;
  1798. USHORT snap_ci, std_ci;
  1799. short snap_cvt;
  1800. std_ci = CutInSize(width, std_width, std_tres, GetUPEM(t1m));
  1801. if (snap!=UNDEF) {
  1802. snap_ci = CutInSize(width, TransY(t1m, t1m->stemsnaph[snap]),
  1803. std_tres, GetUPEM(t1m));
  1804. snap_cvt = (short)SNAPH_CVT(t1m, snap);
  1805. ta = (short)SnapStemArgs(args, (USHORT)ta,
  1806. width, STDH_CVT, (USHORT)snap_cvt,
  1807. std_ci, snap_ci, storage);
  1808. } else {
  1809. ta = (short)StdStemArgs(args, (USHORT)ta, width, STDH_CVT,
  1810. std_ci, storage);
  1811. }
  1812. }
  1813. if (ta+2>(short)(*maxstack)) /* Args + loopcnt + fun_num */
  1814. (*maxstack) = (USHORT)(ta+2);
  1815. CHECK_PREPSIZE(prep, tp, ta*2+2, psize);
  1816. AssembleArgs(args, ta, prep, &tp);
  1817. if (t1m->snaph_cnt)
  1818. tp = (short)CreateSnapStems(prep, (USHORT)tp, (short)weight->cnt_hw);
  1819. else
  1820. tp = (short)CreateStdStems(prep, (USHORT)tp, (short)weight->cnt_hw);
  1821. /******
  1822. * Compute width of vertical stems.
  1823. */
  1824. prep[tp++] = op_svcta | SUBOP_X;
  1825. if ((std_width = GetStdVW(t1m))==0)
  1826. std_width = GetDefStdVW(t1m);
  1827. std_width = TransX(t1m, std_width);
  1828. std_tres = (USHORT)(GetUPEM(t1m) * 2 / std_width);
  1829. ta = 0;
  1830. CHECK_ARGSIZE(args, ta, STEMSNAPARGS*weight->cnt_vw, argsize);
  1831. for (i=0; i<weight->cnt_vw; i++) {
  1832. funit width = TransX(t1m, weight->vwidths[i].width);
  1833. short storage = (short)weight->vwidths[i].storage;
  1834. short snap = GetSnapV(t1m, weight->vwidths[i].width);
  1835. USHORT snap_ci, std_ci;
  1836. short snap_cvt;
  1837. std_ci = CutInSize(width, std_width, std_tres, GetUPEM(t1m));
  1838. if (snap!=UNDEF) {
  1839. snap_ci = CutInSize(width, TransX(t1m, t1m->stemsnapv[snap]),
  1840. std_tres, GetUPEM(t1m));
  1841. snap_cvt = (short)SNAPV_CVT(snap);
  1842. ta = (short)SnapStemArgs(args, (USHORT)ta,
  1843. width, STDV_CVT, (USHORT)snap_cvt,
  1844. std_ci, snap_ci, (USHORT)storage);
  1845. } else {
  1846. ta = (short)StdStemArgs(args, (USHORT)ta, width,
  1847. STDV_CVT, std_ci, (USHORT)storage);
  1848. }
  1849. }
  1850. if (ta+2>(short)(*maxstack))
  1851. (*maxstack) = (USHORT)(ta+2);
  1852. CHECK_PREPSIZE(prep, tp, ta*2+2, psize);
  1853. AssembleArgs(args, ta, prep, &tp);
  1854. if (t1m->snapv_cnt)
  1855. tp = (short)CreateSnapStems(prep, (USHORT)tp, (short)weight->cnt_vw);
  1856. else
  1857. tp = (short)CreateStdStems(prep, (USHORT)tp, (short)weight->cnt_vw);
  1858. prep[tp++] = op_rtg;
  1859. /******
  1860. * Compute diagonal control parameters.
  1861. */
  1862. CHECK_PREPSIZE(prep, tp, STACKINC, psize);
  1863. if ((stdvw = GetStdVW(t1m))==0)
  1864. stdvw = GetDefStdVW(t1m);
  1865. if ((stdhw = GetStdHW(t1m))==0)
  1866. stdhw = GetDefStdHW(t1m);
  1867. if (stdvw && stdhw) {
  1868. cis = (short)(MAX((GetUPEM(t1m) + GetUPEM(t1m)/2) / std_width, 1));
  1869. #ifdef SYMETRICAL_REDUCTION
  1870. shift = (long)GetUPEM(t1m);
  1871. #else
  1872. shift = (long)GetUPEM(t1m)*(long)MIN(stdvw,stdhw)/
  1873. (long)MAX(stdvw, stdhw)/2L+(long)GetUPEM(t1m)/2L;
  1874. #endif
  1875. } else if (stdvw || stdhw) {
  1876. cis = (short)(1548 / MAX(stdvw, stdhw) + 1);
  1877. shift = (long)GetUPEM(t1m)/2;
  1878. } else {
  1879. cis = 41;
  1880. shift = GetUPEM(t1m)/4;
  1881. }
  1882. prep[tp++] = op_pushb1; prep[tp++] = STORAGE_DIAG;
  1883. prep[tp++] = op_pushb1; prep[tp++] = STDV_CVT;
  1884. prep[tp++] = op_rcvt;
  1885. prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)48;
  1886. prep[tp++] = op_lt;
  1887. prep[tp++] = op_if;
  1888. #ifdef SYMETRICAL_REDUCTION
  1889. /* Compute the reduction. */
  1890. shift = (short)(shift/(long)cis/4);
  1891. prep[tp++] = op_npushw;
  1892. prep[tp++] = 2;
  1893. prep[tp++] = (UBYTE)TMPCVT;
  1894. prep[tp++] = 0;
  1895. prep[tp++] = HIBYTE(shift);
  1896. prep[tp++] = LOBYTE(shift);
  1897. prep[tp++] = op_wcvtf;
  1898. prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)TMPCVT;
  1899. prep[tp++] = op_rcvt;
  1900. prep[tp++] = op_pushb1; prep[tp++] = MIN_REDUCTION;
  1901. prep[tp++] = op_add;
  1902. #else
  1903. /* Compute the reduction. */
  1904. shift = (short)(shift/(long)cis/2);
  1905. prep[tp++] = op_npushw;
  1906. prep[tp++] = 2;
  1907. prep[tp++] = (UBYTE)TMPCVT;
  1908. prep[tp++] = 0;
  1909. prep[tp++] = HIBYTE(shift);
  1910. prep[tp++] = LOBYTE(shift);
  1911. prep[tp++] = op_wcvtf;
  1912. prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)TMPCVT;
  1913. prep[tp++] = op_rcvt;
  1914. prep[tp++] = op_pushb1; prep[tp++] = REDUCTION_C1;
  1915. prep[tp++] = op_max;
  1916. #endif
  1917. prep[tp++] = op_else;
  1918. prep[tp++] = op_pushb1; prep[tp++] = 0;
  1919. prep[tp++] = op_eif;
  1920. prep[tp++] = op_pushb1 + 1;
  1921. prep[tp++] = VERSION_1_5;
  1922. prep[tp++] = VERSION_SELECTOR;
  1923. prep[tp++] = op_getinfo;
  1924. prep[tp++] = op_gt;
  1925. prep[tp++] = op_if;
  1926. prep[tp++] = op_pushb1;
  1927. prep[tp++] = 8;
  1928. prep[tp++] = op_mul;
  1929. prep[tp++] = op_eif;
  1930. prep[tp++] = op_ws;
  1931. Free(args);
  1932. }
  1933. (*glob_prep) = prep;
  1934. return (USHORT)tp;
  1935. }
  1936. /***
  1937. ** Function: GetFontProg
  1938. **
  1939. ** Description:
  1940. ** Return the font program.
  1941. ***/
  1942. const UBYTE *GetFontProg(void)
  1943. {
  1944. return tt_GetFontProg();
  1945. }
  1946. /***
  1947. ** Function: GetFontProgSize
  1948. **
  1949. ** Description:
  1950. ** Return the size of the font program.
  1951. ***/
  1952. const USHORT GetFontProgSize(void)
  1953. {
  1954. return tt_GetFontProgSize();
  1955. }
  1956. /***
  1957. ** Function: GetNumFuns
  1958. **
  1959. ** Description:
  1960. ** Return the number of functions defined in
  1961. ** the font program.
  1962. ***/
  1963. const USHORT GetNumFuns(void)
  1964. {
  1965. return tt_GetNumFuns();
  1966. }