#include "dispmisc.h" #include "lsdnode.h" #include "lssubl.h" static long AddSublineAdvanceWidth(PLSSUBL plssubl); static PLSDNODE AdvanceToNextVisualDnodeCore(PLSDNODE, LSTFLOW, POINTUV*); static PLSDNODE NextVisualDnodeOnTheLevel(PLSDNODE pdn, LSTFLOW lstflowMain); #define fUVerticalPlusVDirection (fUVertical|fVDirection) // see comments in lstfset.c // Has this dnode submitted subline(s) for display? #define FIsSubmittingDnode(pdn) (FIsDnodeReal(pdn) && (pdn)->u.real.pinfosubl != NULL && \ (pdn)->u.real.pinfosubl->fUseForDisplay) // Has this dnode accepted subline(s) for display? #define FIsAcceptingDnode(pdn) (FIsDnodeReal(pdn) && (pdn)->u.real.pinfosubl != NULL && \ ((pdn)->u.real.pinfosubl->rgpsubl)[0]->fAcceptedForDisplay) // %%Function: CreateDisplayTree // %%Contact: victork // /* CreateDisplayTree sets plsdnUpTemp in sublines to be displayed with given subline, * rejects wrong sublines, submitted for display, sets fAcceptedForDisplay in good ones */ void CreateDisplayTree(PLSSUBL plssubl) { LSTFLOW lstflowMain = plssubl->lstflow; PLSDNODE pdn = plssubl->plsdnFirst; long dupSum; BOOL fAccept; DWORD i; LSTFLOW lstflowSubline; while (pdn != NULL) /* don't care about break */ { if (FIsSubmittingDnode(pdn)) { Assert(pdn->u.real.pinfosubl->cSubline > 0); fAccept = fTrue; lstflowSubline = ((pdn->u.real.pinfosubl->rgpsubl)[0])->lstflow; // reject if one tflow is vertical, another is horizontal or v-directions are not the same // (see explanation of bits meaning in lstfset.c) if ((lstflowSubline ^ lstflowMain) & fUVerticalPlusVDirection) { fAccept = fFalse; } dupSum = 0; for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++) { dupSum += AddSublineAdvanceWidth((pdn->u.real.pinfosubl->rgpsubl)[i]); // all tflows should be the same if (((pdn->u.real.pinfosubl->rgpsubl)[i])->lstflow != lstflowSubline) { fAccept = fFalse; } // Submitting empty sublines is prohibited if (((pdn->u.real.pinfosubl->rgpsubl)[i])->plsdnFirst == NULL) { fAccept = fFalse; } } // reject if sublines don't sum up to the dnode width if (dupSum != pdn->u.real.dup) { fAccept = fFalse; } if (fAccept) { for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++) { ((pdn->u.real.pinfosubl->rgpsubl)[i])->plsdnUpTemp = pdn; ((pdn->u.real.pinfosubl->rgpsubl)[i])->fAcceptedForDisplay = fTrue; CreateDisplayTree((pdn->u.real.pinfosubl->rgpsubl)[i]); } } } pdn = pdn->plsdnNext; } } // %%Function: DestroyDisplayTree // %%Contact: victork // /* * DestroyDisplayTree nulls plsdnUpTemp in sublines displayed with given subline. */ void DestroyDisplayTree(PLSSUBL plssubl) { PLSDNODE pdn = plssubl->plsdnFirst; DWORD i; while (pdn != NULL) /* don't care about break */ { if (FIsAcceptingDnode(pdn)) { for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++) { ((pdn->u.real.pinfosubl->rgpsubl)[i])->plsdnUpTemp = NULL; ((pdn->u.real.pinfosubl->rgpsubl)[i])->fAcceptedForDisplay = fFalse; DestroyDisplayTree((pdn->u.real.pinfosubl->rgpsubl)[i]); } } pdn = pdn->plsdnNext; } } // %%Function: AdvanceToNextDnode // %%Contact: victork // /* * Advance to the next (visual) node and update pen position, skipping submitting dnodes. */ PLSDNODE AdvanceToNextDnode(PLSDNODE pdn, LSTFLOW lstflowMain, POINTUV* pptpen) { // move to the next pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen); // skip submitting dnodes while (pdn != NULL && FIsAcceptingDnode(pdn)) { pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen); } return pdn; } // %%Function: AdvanceToFirstDnode // %%Contact: victork // PLSDNODE AdvanceToFirstDnode(PLSSUBL plssubl, LSTFLOW lstflowMain, POINTUV* pptpen) { PLSDNODE pdn = plssubl->plsdnFirst; if (pdn != NULL && FIsAcceptingDnode(pdn)) { pdn = AdvanceToNextDnode(pdn, lstflowMain, pptpen); } return pdn; } // %%Function: AdvanceToNextSubmittingDnode // %%Contact: victork // /* * Advance to the next (visual) node and update pen position, stopping only at submitting dnodes. */ PLSDNODE AdvanceToNextSubmittingDnode(PLSDNODE pdn, LSTFLOW lstflowMain, POINTUV* pptpen) { // move to the next pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen); // skip non-submitting dnodes while (pdn != NULL && !FIsAcceptingDnode(pdn)) { pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen); } return pdn; } // %%Function: AdvanceToFirstSubmittingDnode // %%Contact: victork // PLSDNODE AdvanceToFirstSubmittingDnode(PLSSUBL plssubl, LSTFLOW lstflowMain, POINTUV* pptpen) { PLSDNODE pdn = plssubl->plsdnFirst; if (pdn != NULL && !FIsAcceptingDnode(pdn)) { pdn = AdvanceToNextSubmittingDnode(pdn, lstflowMain, pptpen); } return pdn; } // %%Function: AdvanceToNextVisualDnodeCore // %%Contact: victork // /* * Advance to the next node and update pen position * Goes into sublines, submitted for display, traversing the whole display tree. * Stops at dnodes that submitted subline on the way down, skips them going up, so that * every dnode is visited once with pen position at the start of it in visual order. */ static PLSDNODE AdvanceToNextVisualDnodeCore(PLSDNODE pdn, LSTFLOW lstflowMain, POINTUV* pptpen) { PLSDNODE pdnNextVisual, pdnTop; PLSSUBL plssublCurrent; long cSublines, i; PLSSUBL* rgpsubl; if (FIsAcceptingDnode(pdn)) { // Last time we stopped at submitting dnode - // now don't move pen point, go down to the VisualStart of the VisualFirst subline. rgpsubl = pdn->u.real.pinfosubl->rgpsubl; cSublines = pdn->u.real.pinfosubl->cSubline; if (rgpsubl[0]->lstflow == lstflowMain) { pdnNextVisual = rgpsubl[0]->plsdnFirst; } else { pdnNextVisual = rgpsubl[cSublines - 1]->plsdnLastDisplay; } } else { // update pen position - we always move to the (visual) right, all vs are the same tflow if (pdn->klsdn == klsdnReal) { pptpen->u += pdn->u.real.dup; } else { pptpen->u += pdn->u.pen.dup; pptpen->v += pdn->u.pen.dvp; } plssublCurrent = pdn->plssubl; // go to the next dnode of the current subline in visual order pdnNextVisual = NextVisualDnodeOnTheLevel(pdn, lstflowMain); // If current subline is ended, (try) change subline. if (pdnNextVisual == NULL) { // Change subline // // In the loop: pdnNextVisual != NULL signals that next dnode is successfully found. // If pdnNextVisual == NULL, plssublCurrent is the subline just exhausted. // One run of the loop replaces current subline with another subline on the same level // (such change always ends the loop) or with parent subline. while (pdnNextVisual == NULL && plssublCurrent->plsdnUpTemp != NULL) { // find (the index of) the current subline in the list of submitted sublines pdnTop = plssublCurrent->plsdnUpTemp; rgpsubl = pdnTop->u.real.pinfosubl->rgpsubl; cSublines = pdnTop->u.real.pinfosubl->cSubline; for (i=0; i < cSublines && plssublCurrent != rgpsubl[i]; i++); Assert(i < cSublines); // do we have "next" subline? If we do, pdnNextVisual we seek "starts" it. if (pdnTop->plssubl->lstflow == lstflowMain) { i++; if (i < cSublines) { plssublCurrent = rgpsubl[i]; pdnNextVisual = plssublCurrent->plsdnFirst; } } else { i--; if (i >= 0) { plssublCurrent = rgpsubl[i]; pdnNextVisual = plssublCurrent->plsdnLastDisplay; } } // We don't, let's try next dnode on the upper level. if (pdnNextVisual == NULL) { plssublCurrent = pdnTop->plssubl; pdnNextVisual = NextVisualDnodeOnTheLevel(pdnTop, lstflowMain); } } } } return pdnNextVisual; } // %%Function: NextVisualDnodeOnTheLevel // %%Contact: victork // // find next dnode on the level moving right or left, signalling end with a NULL static PLSDNODE NextVisualDnodeOnTheLevel(PLSDNODE pdn, LSTFLOW lstflowMain) { if (pdn->plssubl->lstflow == lstflowMain) { if (pdn == pdn->plssubl->plsdnLastDisplay) { return NULL; } else { return pdn->plsdnNext; } } return pdn->plsdnPrev; } // %%Function: AddSublineAdvanceWidth // %%Contact: victork // // Note: It is not subline width as calculated in GetObjDimSubline static long AddSublineAdvanceWidth(PLSSUBL plssubl) { long dupSum; PLSDNODE pdn; pdn = plssubl->plsdnFirst; dupSum = 0; while (pdn != NULL) { if (pdn->klsdn == klsdnReal) { dupSum += pdn->u.real.dup; } else /* pen, border */ { dupSum += pdn->u.pen.dup; } if (pdn == plssubl->plsdnLastDisplay) { pdn = NULL; } else { pdn = pdn->plsdnNext; Assert(pdn != NULL); // plsdnLastDisplay should prevent this } } return dupSum; } // NB Victork - following functions were used only for upClipLeft, upClipRight optimization. // If we'll decide that we do need that optimization after Word integration - I'll uncomment. #ifdef NEVER // %%Function: RectUVFromRectXY // %%Contact: victork // // There is an assymetry in the definition of the rectangle. // (Left, Top) belongs to rectangle and (Right, Bottom) doesn't, // It makes following procedures hard to understand and write. // So I first cut off the points that don't belong, then turn the rectangle, then add extra // points again and hope compiler will make it fast. // RectUVFromRectXY calculates (clip) rectangle in local (u,v) coordinates given // (clip) rectangle in (x,y) and point of origin void RectUVFromRectXY(const POINT* pptXY, /* IN: point of origin for local coordinates (x,y) */ const RECT* prectXY, /* IN: input rectangle (x,y) */ LSTFLOW lstflow, /* IN: local text flow */ RECTUV* prectUV) /* OUT: output rectangle (u,v) */ { switch (lstflow) { case lstflowES: /* latin */ prectUV->upLeft = (prectXY->left - pptXY->x); prectUV->upRight = (prectXY->right - 1 - pptXY->x) + 1; prectUV->vpTop = -(prectXY->top - pptXY->y); prectUV->vpBottom = -(prectXY->bottom - 1 - pptXY->y) - 1; return; case lstflowSW: /* vertical FE */ prectUV->upLeft = (prectXY->top - pptXY->y); prectUV->upRight = (prectXY->bottom - 1 - pptXY->y) + 1; prectUV->vpTop = (prectXY->right - 1 - pptXY->x); prectUV->vpBottom = (prectXY->left - pptXY->x) - 1; return; case lstflowWS: /* BiDi */ prectUV->upLeft = -(prectXY->right - 1 - pptXY->x); prectUV->upRight = -(prectXY->left - pptXY->x) + 1; prectUV->vpTop = -(prectXY->top - pptXY->y); prectUV->vpBottom = -(prectXY->bottom - 1 - pptXY->y) - 1; return; case lstflowEN: prectUV->upLeft = (prectXY->left - pptXY->x); prectUV->upRight = (prectXY->right - 1 - pptXY->x) + 1; prectUV->vpTop = (prectXY->bottom - 1 - pptXY->y); prectUV->vpBottom = (prectXY->top - pptXY->y) - 1; return; case lstflowSE: prectUV->upLeft = (prectXY->top - pptXY->y); prectUV->upRight = (prectXY->bottom - 1 - pptXY->y) + 1; prectUV->vpTop = -(prectXY->left - pptXY->x); prectUV->vpBottom = -(prectXY->right - 1 - pptXY->x) - 1; return; case lstflowWN: prectUV->upLeft = -(prectXY->right - 1 - pptXY->x); prectUV->upRight = -(prectXY->left - pptXY->x) + 1; prectUV->vpTop = (prectXY->bottom - 1 - pptXY->y); prectUV->vpBottom = (prectXY->top - pptXY->y) - 1; return; case lstflowNE: prectUV->upLeft = -(prectXY->bottom - 1 - pptXY->y); prectUV->upRight = -(prectXY->top - pptXY->y) + 1; prectUV->vpTop = -(prectXY->left - pptXY->x); prectUV->vpBottom = -(prectXY->right - 1 - pptXY->x) - 1; return; case lstflowNW: prectUV->upLeft = -(prectXY->bottom - 1 - pptXY->y); prectUV->upRight = -(prectXY->top - pptXY->y) + 1; prectUV->vpTop = (prectXY->right - 1 - pptXY->x); prectUV->vpBottom = (prectXY->left - pptXY->x) - 1; return; default: NotReached(); } } // %%Function: RectXYFromRectUV // %%Contact: victork // // RectXYFromRectUV calculates rectangle in (x,y) coordinates given rectangle in local (u,v) // and point of origin (x,y) for local coordinate system void RectXYFromRectUV(const POINT* pptXY, /* IN: point of origin for local coordinates (x,y) */ PCRECTUV prectUV, /* IN: input rectangle (u,v) */ LSTFLOW lstflow, /* IN: local text flow */ RECT* prectXY) /* OUT: output rectangle (x,y) */ { switch (lstflow) { case lstflowES: /* latin */ prectXY->left = pptXY->x + prectUV->upLeft; prectXY->right = pptXY->x + (prectUV->upRight - 1) + 1; prectXY->top = pptXY->y - (prectUV->vpTop); prectXY->bottom = pptXY->y - (prectUV->vpBottom + 1) + 1; return; case lstflowSW: /* vertical FE */ prectXY->left = pptXY->x + (prectUV->vpBottom + 1); prectXY->right = pptXY->x + (prectUV->vpTop) + 1; prectXY->top = pptXY->y + prectUV->upLeft; prectXY->bottom = pptXY->y + (prectUV->upRight - 1) + 1; return; case lstflowWS: /* BiDi */ prectXY->left = pptXY->x - (prectUV->upRight - 1); prectXY->right = pptXY->x - prectUV->upLeft + 1; prectXY->top = pptXY->y - (prectUV->vpTop); prectXY->bottom = pptXY->y - (prectUV->vpBottom + 1) + 1; return; case lstflowEN: prectXY->left = pptXY->x + prectUV->upLeft; prectXY->right = pptXY->x + (prectUV->upRight - 1) + 1; prectXY->top = pptXY->y + (prectUV->vpBottom + 1); prectXY->bottom = pptXY->y + (prectUV->vpTop) + 1; return; case lstflowSE: prectXY->left = pptXY->x - (prectUV->vpTop); prectXY->right = pptXY->x - (prectUV->vpBottom + 1) + 1; prectXY->top = pptXY->y + prectUV->upLeft; prectXY->bottom = pptXY->y + (prectUV->upRight - 1) + 1; return; case lstflowWN: prectXY->left = pptXY->x - (prectUV->upRight - 1); prectXY->right = pptXY->x - prectUV->upLeft + 1; prectXY->top = pptXY->y + (prectUV->vpBottom + 1); prectXY->bottom = pptXY->y + (prectUV->vpTop) + 1; return; case lstflowNE: prectXY->left = pptXY->x - (prectUV->vpTop); prectXY->right = pptXY->x - (prectUV->vpBottom + 1) + 1; prectXY->top = pptXY->y - (prectUV->upRight - 1); prectXY->bottom = pptXY->y - prectUV->upLeft + 1; return; case lstflowNW: prectXY->left = pptXY->x + (prectUV->vpBottom + 1); prectXY->right = pptXY->x + (prectUV->vpTop) + 1; prectXY->top = pptXY->y - (prectUV->upRight - 1); prectXY->bottom = pptXY->y - prectUV->upLeft + 1; return; default: NotReached(); } } #endif /* NEVER */