Leaked source code of windows server 2003
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.

634 lines
18 KiB

  1. #include "lsqcore.h"
  2. #include "lsc.h"
  3. #include "lsqsinfo.h"
  4. #include "lsdnode.h"
  5. #include "lssubl.h"
  6. #include "heights.h"
  7. #include "lschp.h"
  8. #include "iobj.h"
  9. #include "lsqin.h"
  10. #include "lsqout.h"
  11. #include "dninfo.h"
  12. #include "lssubset.h"
  13. #include "lstfset.h"
  14. #include "dispmisc.h"
  15. #define FIsInContent(pdn) (!FIsNotInContent(pdn))
  16. #define FIsZeroWidth(pdn) (FIsDnodeReal(pdn) && (pdn)->u.real.dup == 0)
  17. #define FIsDnodeClosingBorder(pdn) (FIsDnodeBorder(pdn) && (!(pdn)->fOpenBorder))
  18. static void PrepareQueryCall(PLSSUBL, PLSDNODE, LSQIN*);
  19. static LSERR FillInQueryResults(PLSC, PLSSUBL, PLSQSUBINFO, PLSDNODE, POINTUV*, LSQOUT*);
  20. static void FillInTextCellInfo(PLSC, PLSDNODE, POINTUV*, LSQOUT*, PLSTEXTCELL);
  21. static void TransformPointsOnLowerLevels(PLSQSUBINFO, DWORD, PLSTEXTCELL, PPOINTUV, LSTFLOW, LSTFLOW);
  22. static void ApplyFormula(PPOINTUV, DWORD[], PPOINTUV);
  23. static PLSDNODE BacktrackToPreviousDnode(PLSDNODE pdn, POINTUV* pt);
  24. static PLSDNODE AdvanceToNextDnodeQuery(PLSDNODE, PPOINTUV);
  25. // %%Function: QuerySublineCpPpointCore
  26. // %%Contact: victork
  27. //
  28. /*
  29. * Returns dim-info of the cp in the subline.
  30. *
  31. * If that cp isn't displayed in the line, take closest to the left that is displayed.
  32. * If that's impossible, go to the right.
  33. *
  34. * Hidden text inside ligature makes it impossible to tell whether a particular cp is hidden or not
  35. */
  36. LSERR QuerySublineCpPpointCore(
  37. PLSSUBL plssubl,
  38. LSCP cp, /* IN: cpQuery */
  39. DWORD cDepthQueryMax, /* IN: allocated size of results array */
  40. PLSQSUBINFO plsqsubinfoResults, /* OUT: array[cDepthQueryMax] of results */
  41. DWORD* pcActualDepth, /* OUT: size of results array (filled) */
  42. PLSTEXTCELL plstextcell) /* OUT: Text cell info */
  43. {
  44. PLSC plsc;
  45. LSERR lserr = lserrNone;
  46. PLSDNODE pdn, pdnPrev = NULL;
  47. POINTUV pt;
  48. LSCP cpLim;
  49. LSQIN lsqin;
  50. LSQOUT lsqout;
  51. PLSSUBL plssublLowerLevels;
  52. POINTUV ptStartLowerLevels;
  53. PLSQSUBINFO plsqsubinfoLowerLevels;
  54. DWORD cDepthQueryMaxLowerLevels;
  55. DWORD cActualDepthLowerLevels;
  56. Assert(FIsLSSUBL(plssubl));
  57. Assert(!plssubl->fDupInvalid);
  58. if (cDepthQueryMax == 0)
  59. {
  60. return lserrInsufficientQueryDepth;
  61. }
  62. plsc = plssubl->plsc;
  63. cpLim = plssubl->cpLimDisplay;
  64. pt.u = 0;
  65. pt.v = 0;
  66. pdn = plssubl->plsdnFirst;
  67. /* Skip over autonumbers & starting pens/borders */
  68. while (FDnodeBeforeCpLim(pdn, cpLim) && (FIsNotInContent(pdn) || !(FIsDnodeReal(pdn))))
  69. {
  70. pdn = AdvanceToNextDnodeQuery(pdn, &pt);
  71. }
  72. if (!FDnodeBeforeCpLim(pdn, cpLim))
  73. { /* empty subline */
  74. *pcActualDepth = 0;
  75. return lserrNone;
  76. }
  77. // if cp <= pdn->cpFirst, pdn is the dnode to query, else...
  78. if (cp > pdn->cpFirst)
  79. {
  80. /* Skip dnodes before the cp */
  81. while (FDnodeBeforeCpLim(pdn, cpLim) && pdn->cpFirst + pdn->dcp <= (LSDCP)cp)
  82. {
  83. pdnPrev = pdn;
  84. pdn = AdvanceToNextDnodeQuery(pdn, &pt);
  85. }
  86. /* go back if our cp is in vanished text or pen or border */
  87. if (!FDnodeBeforeCpLim(pdn, cpLim) || // reached the end
  88. pdn->cpFirst > cp || // went too far because of hidden text
  89. !(FIsDnodeReal(pdn))) // our cp points to a pen
  90. {
  91. Assert(pdnPrev != NULL); // we made at least one forward step
  92. pdn = pdnPrev;
  93. pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
  94. // skip all pens/borders
  95. while (pdn != NULL && FIsInContent(pdn) && !(FIsDnodeReal(pdn)))
  96. {
  97. pdn = pdnPrev;
  98. pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
  99. }
  100. // nothing good to the left situation is impossible
  101. Assert(pdn != NULL && !FIsNotInContent(pdn));
  102. }
  103. }
  104. /* we've found the dnode, have pt just before it, ask method for details */
  105. if (cp >= (LSCP) (pdn->cpFirst + pdn->dcp)) /* cp in next vanished piece */
  106. cp = pdn->cpFirst + pdn->dcp - 1; /* query last cp */
  107. if (cp < (LSCP) pdn->cpFirst) /* cp in a previous pen */
  108. cp = pdn->cpFirst; /* query first cp */
  109. pt.v += pdn->u.real.lschp.dvpPos; // go to the local baseline
  110. PrepareQueryCall(plssubl, pdn, &lsqin);
  111. lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnQueryCpPpoint)
  112. (pdn->u.real.pdobj, cp - pdn->cpFirst, &lsqin, &lsqout);
  113. if (lserr != lserrNone)
  114. return lserr;
  115. lserr = FillInQueryResults(plsc, plssubl, plsqsubinfoResults, pdn, &pt, &lsqout);
  116. if (lserr != lserrNone)
  117. return lserr;
  118. if (lsqout.plssubl == NULL) // terminal object
  119. {
  120. *pcActualDepth = 1;
  121. FillInTextCellInfo(plsc, pdn, &pt, &lsqout, plstextcell);
  122. }
  123. else // there are more level(s)
  124. {
  125. // recursive call to fill lower levels
  126. plssublLowerLevels = lsqout.plssubl;
  127. plsqsubinfoLowerLevels = plsqsubinfoResults + 1;
  128. cDepthQueryMaxLowerLevels = cDepthQueryMax - 1;
  129. lserr = QuerySublineCpPpointCore(plssublLowerLevels, cp, cDepthQueryMaxLowerLevels,
  130. plsqsubinfoLowerLevels, &cActualDepthLowerLevels, plstextcell);
  131. if (lserr != lserrNone)
  132. return lserr;
  133. *pcActualDepth = cActualDepthLowerLevels + 1;
  134. ptStartLowerLevels.u = pt.u + lsqout.pointUvStartSubline.u;
  135. ptStartLowerLevels.v = pt.v + lsqout.pointUvStartSubline.v;
  136. TransformPointsOnLowerLevels(plsqsubinfoLowerLevels, cActualDepthLowerLevels, plstextcell,
  137. &ptStartLowerLevels, plssubl->lstflow, plssublLowerLevels->lstflow);
  138. }
  139. return lserrNone;
  140. }
  141. // %%Function: QuerySublinePointPcpCore
  142. // %%Contact: victork
  143. //
  144. /*
  145. * Returns dim-info of the cp in the line, that a) contains given point or
  146. * b) is closest to it from the left or
  147. * c) is just closest to it
  148. */
  149. LSERR QuerySublinePointPcpCore(
  150. PLSSUBL plssubl,
  151. PCPOINTUV pptIn,
  152. DWORD cDepthQueryMax, /* IN: allocated size of results array */
  153. PLSQSUBINFO plsqsubinfoResults, /* OUT: array[cDepthQueryMax] of results */
  154. DWORD* pcActualDepth, /* OUT: size of results array (filled) */
  155. PLSTEXTCELL plstextcell) /* OUT: Text cell info */
  156. {
  157. PLSC plsc;
  158. LSERR lserr = lserrNone;
  159. PLSDNODE pdn, pdnPrev = NULL;
  160. POINTUV pt, ptInside, ptInsideLocal;
  161. LSCP cpLim;
  162. LSQIN lsqin;
  163. LSQOUT lsqout;
  164. PLSSUBL plssublLowerLevels;
  165. POINTUV ptStartLowerLevels;
  166. PLSQSUBINFO plsqsubinfoLowerLevels;
  167. DWORD cDepthQueryMaxLowerLevels;
  168. DWORD cActualDepthLowerLevels;
  169. long upQuery;
  170. Assert(FIsLSSUBL(plssubl));
  171. Assert(!plssubl->fDupInvalid);
  172. if (cDepthQueryMax == 0)
  173. {
  174. return lserrInsufficientQueryDepth;
  175. }
  176. plsc = plssubl->plsc;
  177. cpLim = plssubl->cpLimDisplay;
  178. pt.u = 0;
  179. pt.v = 0;
  180. pdn = plssubl->plsdnFirst;
  181. /* Skip over autonumbers & starting pens & empty dnodes */
  182. while (FDnodeBeforeCpLim(pdn, cpLim) && (FIsNotInContent(pdn) || !(FIsDnodeReal(pdn)) || FIsZeroWidth(pdn)))
  183. {
  184. pdn = AdvanceToNextDnodeQuery(pdn, &pt);
  185. }
  186. if (!FDnodeBeforeCpLim(pdn, cpLim))
  187. { /* empty subline */
  188. *pcActualDepth = 0;
  189. return lserrNone;
  190. }
  191. upQuery = pptIn->u;
  192. /*
  193. * Find dnode with our point inside.
  194. *
  195. * We look only at upQuery to do it.
  196. */
  197. // if pt.u >= upQuery, pdn is the dnode to query, else...
  198. if (pt.u <= upQuery)
  199. {
  200. // skip until the end or dnode to the right of our point
  201. // (That means extra work, but covers zero dup situation without additional if.)
  202. while (FDnodeBeforeCpLim(pdn, cpLim) && pt.u <= upQuery)
  203. {
  204. pdnPrev = pdn;
  205. pdn = AdvanceToNextDnodeQuery(pdn, &pt);
  206. }
  207. if (FIsDnodeBorder(pdnPrev))
  208. {
  209. if (pdnPrev->fOpenBorder)
  210. {
  211. // upQuery was in the previous opening border - pdn is the dnode we need
  212. Assert(FDnodeBeforeCpLim(pdn, cpLim));
  213. }
  214. else
  215. {
  216. // upQuery was in the previous closing border - dnode we need is before the border
  217. pdn = pdnPrev;
  218. Assert(pdn != NULL && !FIsNotInContent(pdn));
  219. pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
  220. pdn = pdnPrev;
  221. Assert(pdn != NULL && !FIsNotInContent(pdn));
  222. pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
  223. }
  224. }
  225. else
  226. {
  227. /* go back to the previous dnode */
  228. pdn = pdnPrev;
  229. pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
  230. // if it is a pen/border or empty dnode (non-req hyphen), skip them all
  231. // (Border cannot be the previous dnode, but is possble later)
  232. while (pdn != NULL && (!(FIsDnodeReal(pdn)) || FIsZeroWidth(pdn)))
  233. {
  234. pdn = pdnPrev;
  235. pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
  236. }
  237. // "nothing good to the left" situation is impossible
  238. Assert(pdn != NULL && !FIsNotInContent(pdn));
  239. }
  240. }
  241. // We have found the leftmost dnode with our dup to the right of it
  242. // pt is just before it, ask method for details
  243. pt.v += pdn->u.real.lschp.dvpPos; // go to the local baseline
  244. PrepareQueryCall(plssubl, pdn, &lsqin);
  245. // get query point relative to the starting point of the dnode
  246. // we give no guarantee that it is really inside dnode box
  247. ptInside.u = pptIn->u - pt.u;
  248. ptInside.v = pptIn->v - pt.v;
  249. lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnQueryPointPcp)
  250. (pdn->u.real.pdobj, &ptInside, &lsqin, &lsqout);
  251. if (lserr != lserrNone)
  252. return lserr;
  253. lserr = FillInQueryResults(plsc, plssubl, plsqsubinfoResults, pdn, &pt, &lsqout);
  254. if (lserr != lserrNone)
  255. return lserr;
  256. if (lsqout.plssubl == NULL) // terminal object
  257. {
  258. *pcActualDepth = 1;
  259. FillInTextCellInfo(plsc, pdn, &pt, &lsqout, plstextcell);
  260. }
  261. else // there are more level(s)
  262. {
  263. // recursive call to fill lower levels
  264. plssublLowerLevels = lsqout.plssubl;
  265. plsqsubinfoLowerLevels = plsqsubinfoResults + 1;
  266. cDepthQueryMaxLowerLevels = cDepthQueryMax - 1;
  267. // get query point in lower level subline coordinate system
  268. lserr = LsPointUV2FromPointUV1(plssubl->lstflow, &(lsqout.pointUvStartSubline), &ptInside, /* IN: end input point (TF1) */
  269. plssublLowerLevels->lstflow, &ptInsideLocal);
  270. if (lserr != lserrNone)
  271. return lserr;
  272. lserr = QuerySublinePointPcpCore(plssublLowerLevels, &ptInsideLocal, cDepthQueryMaxLowerLevels,
  273. plsqsubinfoLowerLevels, &cActualDepthLowerLevels, plstextcell);
  274. if (lserr != lserrNone)
  275. return lserr;
  276. *pcActualDepth = cActualDepthLowerLevels + 1;
  277. ptStartLowerLevels.u = pt.u + lsqout.pointUvStartSubline.u;
  278. ptStartLowerLevels.v = pt.v + lsqout.pointUvStartSubline.v;
  279. TransformPointsOnLowerLevels(plsqsubinfoLowerLevels, cActualDepthLowerLevels, plstextcell,
  280. &ptStartLowerLevels, plssubl->lstflow, plssublLowerLevels->lstflow);
  281. }
  282. return lserrNone;
  283. }
  284. // %%Function: PrepareQueryCall
  285. // %%Contact: victork
  286. //
  287. static void PrepareQueryCall(PLSSUBL plssubl, PLSDNODE pdn, LSQIN* plsqin)
  288. {
  289. plsqin->lstflowSubline = plssubl->lstflow;
  290. plsqin->plsrun = pdn->u.real.plsrun;
  291. plsqin->cpFirstRun = pdn->cpFirst;
  292. plsqin->dcpRun = pdn->dcp;
  293. plsqin->heightsPresRun = pdn->u.real.objdim.heightsPres;
  294. plsqin->dupRun = pdn->u.real.dup;
  295. plsqin->dvpPosRun = pdn->u.real.lschp.dvpPos;
  296. }
  297. // %%Function: FillInQueryResults
  298. // %%Contact: victork
  299. //
  300. static LSERR FillInQueryResults(
  301. PLSC plsc,
  302. PLSSUBL plssubl,
  303. PLSQSUBINFO plsqsubinfoResults,
  304. PLSDNODE pdn,
  305. POINTUV* ppt,
  306. LSQOUT* plsqout
  307. )
  308. {
  309. OBJDIM objdimSubline;
  310. LSERR lserr;
  311. PLSDNODE pdnNext, pdnPrev;
  312. // fill in subline info
  313. lserr = LssbGetObjDimSubline(plssubl, &(plsqsubinfoResults->lstflowSubline), &objdimSubline);
  314. if (lserr != lserrNone)
  315. return lserr;
  316. lserr = LssbGetDupSubline(plssubl, &(plsqsubinfoResults->lstflowSubline), &plsqsubinfoResults->dupSubline);
  317. if (lserr != lserrNone)
  318. return lserr;
  319. plsqsubinfoResults->cpFirstSubline = plssubl->cpFirst;
  320. plsqsubinfoResults->dcpSubline = plssubl->cpLimDisplay - plssubl->cpFirst;
  321. plsqsubinfoResults->pointUvStartSubline.u = 0;
  322. plsqsubinfoResults->pointUvStartSubline.v = 0;
  323. plsqsubinfoResults->heightsPresSubline = objdimSubline.heightsPres;
  324. // fill in dnode info
  325. if (IdObjFromDnode(pdn) == IobjTextFromLsc(&(plsc->lsiobjcontext)))
  326. plsqsubinfoResults->idobj = idObjText;
  327. else
  328. plsqsubinfoResults->idobj = pdn->u.real.lschp.idObj;
  329. plsqsubinfoResults->plsrun = pdn->u.real.plsrun;
  330. plsqsubinfoResults->cpFirstRun = pdn->cpFirst;
  331. plsqsubinfoResults->dcpRun = pdn->dcp;
  332. plsqsubinfoResults->pointUvStartRun = *ppt; // local baseline
  333. plsqsubinfoResults->heightsPresRun = pdn->u.real.objdim.heightsPres;
  334. plsqsubinfoResults->dupRun = pdn->u.real.dup;
  335. plsqsubinfoResults->dvpPosRun = pdn->u.real.lschp.dvpPos;
  336. // fill in object info
  337. plsqsubinfoResults->pointUvStartObj.u = ppt->u + plsqout->pointUvStartObj.u;
  338. plsqsubinfoResults->pointUvStartObj.v = ppt->v + plsqout->pointUvStartObj.v;
  339. plsqsubinfoResults->heightsPresObj = plsqout->heightsPresObj;
  340. plsqsubinfoResults->dupObj = plsqout->dupObj;
  341. // add borders info
  342. plsqsubinfoResults->dupBorderAfter = 0;
  343. plsqsubinfoResults->dupBorderBefore = 0;
  344. if (pdn->u.real.lschp.fBorder)
  345. {
  346. pdnNext = pdn->plsdnNext;
  347. if (pdnNext != NULL && FIsDnodeClosingBorder(pdnNext))
  348. {
  349. plsqsubinfoResults->dupBorderAfter = pdnNext->u.pen.dup;
  350. }
  351. pdnPrev = pdn->plsdnPrev;
  352. if (pdnPrev != NULL && FIsDnodeOpenBorder(pdnPrev))
  353. {
  354. Assert(FIsInContent(pdnPrev));
  355. plsqsubinfoResults->dupBorderBefore = pdnPrev->u.pen.dup;
  356. }
  357. }
  358. return lserrNone;
  359. }
  360. // %%Function: FillInTextCellInfo
  361. // %%Contact: victork
  362. //
  363. static void FillInTextCellInfo(
  364. PLSC plsc,
  365. PLSDNODE pdn,
  366. POINTUV* ppt,
  367. LSQOUT* plsqout,
  368. PLSTEXTCELL plstextcell /* OUT: Text cell info */
  369. )
  370. {
  371. if (IdObjFromDnode(pdn) == IobjTextFromLsc(&(plsc->lsiobjcontext)))
  372. {
  373. // text has cell info filled - copy it
  374. *plstextcell = plsqout->lstextcell;
  375. // but starting point is relative to the begining of dnode - adjust to that of subline
  376. plstextcell->pointUvStartCell.u += ppt->u;
  377. plstextcell->pointUvStartCell.v += ppt->v;
  378. // adjust cpEndCell if some hidden text got into last ligature - text is unaware of the issue
  379. if (pdn->cpFirst + pdn->dcp < (LSDCP) pdn->cpLimOriginal &&
  380. (LSDCP) plstextcell->cpEndCell == pdn->cpFirst + pdn->dcp - 1)
  381. {
  382. plstextcell->cpEndCell = pdn->cpLimOriginal - 1;
  383. }
  384. // pointer to the dnode to get details quickly - only query manager knows what PCELLDETAILS is
  385. plstextcell->pCellDetails = (PCELLDETAILS)pdn;
  386. }
  387. else
  388. {
  389. // non-text object should not fill lstxtcell, client should not look into it
  390. // I fill it with object information for debug purposes
  391. // Consider zapping it in lsqline later (Rick's suggestion)
  392. plstextcell->cpStartCell = pdn->cpFirst;
  393. plstextcell->cpEndCell = pdn->cpFirst + pdn->dcp - 1;
  394. plstextcell->pointUvStartCell = *ppt;
  395. plstextcell->dupCell = pdn->u.real.dup;
  396. plstextcell->cCharsInCell = 0;
  397. plstextcell->cGlyphsInCell = 0;
  398. plstextcell->pCellDetails = NULL;
  399. }
  400. }
  401. // %%Function: TransformPointsOnLowerLevels
  402. // %%Contact: victork
  403. //
  404. // transform all vectors in results array from lstflow2 to lstflow1, adding pointuvStart (lstflow1)
  405. static void TransformPointsOnLowerLevels(
  406. PLSQSUBINFO plsqsubinfo, /* IN/OUT: results array */
  407. DWORD cDepth, /* IN: size of results array */
  408. PLSTEXTCELL plstextcell, // IN/OUT: text cell
  409. PPOINTUV ppointuvStart, // IN: in lstflow1
  410. LSTFLOW lstflow1, // IN: lstflow1
  411. LSTFLOW lstflow2) // IN: lstflow2
  412. {
  413. // Have to apply formulas
  414. // VectorOut.u = k11 * VectorIn.u + k12 * VectorIn.v + pointuvStart.u
  415. // VectorOut.v = k21 * VectorIn.u + k22 * VectorIn.v + pointuvStart.u
  416. // to several vectors in results array (all elements in k matrix are zero or +/- 1)
  417. // Algorithm: find the matrix first, then use it
  418. DWORD k[4];
  419. POINTUV pointuv0, pointuv1, pointuv2;
  420. pointuv0.u = 0;
  421. pointuv0.v = 0;
  422. pointuv1.u = 1;
  423. pointuv1.v = 0;
  424. LsPointUV2FromPointUV1(lstflow2, &pointuv0, &pointuv1, lstflow1, &pointuv2);
  425. k[0] = pointuv2.u; // k11
  426. k[1] = pointuv2.v; // k21
  427. pointuv1.u = 0;
  428. pointuv1.v = 1;
  429. LsPointUV2FromPointUV1(lstflow2, &pointuv0, &pointuv1, lstflow1, &pointuv2);
  430. k[2] = pointuv2.u; // k12
  431. k[3] = pointuv2.v; // k22
  432. // all points in lower levels are in lstflowLowerLevels (lstflow2) with starting point at the
  433. // beginning of the top lower levels subline
  434. // Translate them to lstflowTop (lstflow1) and starting point of our subline.
  435. while (cDepth > 0)
  436. {
  437. ApplyFormula(&(plsqsubinfo->pointUvStartSubline), k, ppointuvStart);
  438. ApplyFormula(&(plsqsubinfo->pointUvStartRun), k, ppointuvStart);
  439. ApplyFormula(&(plsqsubinfo->pointUvStartObj), k, ppointuvStart);
  440. plsqsubinfo++;
  441. cDepth--;
  442. }
  443. // StartCell point should be adjusted too
  444. ApplyFormula(&(plstextcell->pointUvStartCell), k, ppointuvStart);
  445. }
  446. // %%Function: ApplyFormula
  447. // %%Contact: victork
  448. //
  449. static void ApplyFormula(PPOINTUV ppointuv, DWORD* rgk, PPOINTUV ppointuvStart)
  450. {
  451. POINTUV pointuvTemp;
  452. pointuvTemp.u = ppointuvStart->u + rgk[0] * ppointuv->u + rgk[2] * ppointuv->v;
  453. pointuvTemp.v = ppointuvStart->v + rgk[1] * ppointuv->u + rgk[3] * ppointuv->v;
  454. *ppointuv = pointuvTemp;
  455. }
  456. // %%Function: AdvanceToNextDnodeQuery
  457. // %%Contact: victork
  458. //
  459. /*
  460. * Advance to the next node and update pen position (never goes into sublines)
  461. */
  462. static PLSDNODE AdvanceToNextDnodeQuery(PLSDNODE pdn, POINTUV* ppt)
  463. {
  464. if (pdn->klsdn == klsdnReal)
  465. {
  466. ppt->u += pdn->u.real.dup;
  467. }
  468. else /* case klsdnPen */
  469. {
  470. ppt->u += pdn->u.pen.dup;
  471. ppt->v += pdn->u.pen.dvp;
  472. }
  473. return pdn->plsdnNext;
  474. }
  475. // %%Function: BacktrackToPreviousDnode
  476. // %%Contact: victork
  477. //
  478. // Backtrack and downdate pen position.
  479. // Both parameters are input/output
  480. // Input: dnode number N-1 and point at the beginning of the dnode number N
  481. // Output: point at the beginning of the dnode number N-1
  482. // Return: dnode number N-2
  483. static PLSDNODE BacktrackToPreviousDnode(PLSDNODE pdn, POINTUV* ppt)
  484. {
  485. if (FIsDnodeReal(pdn))
  486. {
  487. ppt->u -= pdn->u.real.dup;
  488. }
  489. else /* it's Pen */
  490. {
  491. ppt->u -= pdn->u.pen.dup;
  492. ppt->v -= pdn->u.pen.dvp;
  493. }
  494. return pdn->plsdnPrev;
  495. }