windows-nt/Source/XPSP1/NT/windows/richedit/lssrc/lstxtmap.c
2020-09-26 16:20:57 +08:00

521 lines
16 KiB
C

#include "lstxtmap.h"
#include "txtinf.h"
#include "txtginf.h"
#include "txtobj.h"
#include "txtils.h"
/* ============================================================== */
/* IgndFirstFromIwch Find first GL index for a given IWCH */
/* */
/* Contact: antons */
/* ============================================================== */
long IgindFirstFromIwch(PTXTOBJ ptxtobj, long iwch)
{
PLNOBJ plnobj = ptxtobj->plnobj;
Assert (FBetween (iwch, ptxtobj->iwchFirst, ptxtobj->iwchLim));
/* Since "pilsobj->pgmap [iwch]" - */
/* GL index is not absolute but RELATIVE to the first run shaped */
/* with ptxtobj "together", we have to calculate required GL index */
/* with the following folmula: */
if (iwch == ptxtobj->iwchLim)
return ptxtobj->igindLim;
else
return ptxtobj->igindFirst + plnobj->pgmap [iwch] -
plnobj->pgmap [ptxtobj->iwchFirst];
}
/* ============================================================== */
/* IgindFirstFromIwchVeryFirst */
/* */
/* Contact: antons */
/* ============================================================== */
long IgindFirstFromIwchVeryFirst (PTXTOBJ ptxtobj, long igindVeryFirst, long iwch)
{
Assert (ptxtobj->iwchLim > ptxtobj->iwchFirst);
return igindVeryFirst + ptxtobj->plnobj->pgmap [iwch];
}
/* ============================================================== */
/* IgindLastFromIwchVeryFirst */
/* */
/* Contact: antons */
/* ============================================================== */
long IgindLastFromIwchVeryFirst (PTXTOBJ ptxtobj, long igindVeryFirst, long iwch)
{
TXTGINF* pginf = ptxtobj->plnobj->pilsobj->pginf;
long igindLast;
Assert (ptxtobj->iwchLim > ptxtobj->iwchFirst);
igindLast = IgindFirstFromIwchVeryFirst (ptxtobj, igindVeryFirst, iwch);
while (! (pginf [igindLast] & ginffLastInContext)) igindLast++;
return igindLast;
}
void GetIgindsFromTxtobj ( PTXTOBJ ptxtobj,
long igindVeryFirst,
long * pigindFirst,
long * pigindLim )
{
PLNOBJ plnobj = ptxtobj->plnobj;
PILSOBJ pilsobj = plnobj->pilsobj;
TXTGINF* pginf = pilsobj->pginf;
long igindLast;
Assert (ptxtobj->iwchLim > ptxtobj->iwchFirst);
Assert (pilsobj->ptxtinf [ptxtobj->iwchFirst].fFirstInContext);
Assert (pilsobj->ptxtinf [ptxtobj->iwchLim-1].fLastInContext);
*pigindFirst = igindVeryFirst + plnobj->pgmap [ptxtobj->iwchFirst];
igindLast = IgindFirstFromIwch (ptxtobj, ptxtobj->iwchLim-1);
while (! (pginf [igindLast] & ginffLastInContext)) igindLast++;
*pigindLim = igindLast + 1;
}
/* ============================================================== */
/* IgndLastFromIwch Find last GL index for a given IWCH */
/* */
/* Contact: antons */
/* ============================================================== */
long IgindLastFromIwch(PTXTOBJ ptxtobj, long iwch)
{
PILSOBJ pilsobj = ptxtobj->plnobj->pilsobj;
TXTGINF* pginf = pilsobj->pginf;
long igindLast;
if (iwch < ptxtobj->iwchFirst)
return -1;
igindLast = IgindFirstFromIwch (ptxtobj, iwch);
Assert (FBetween (iwch, ptxtobj->iwchFirst, ptxtobj->iwchLim-1));
while (! (pginf [igindLast] & ginffLastInContext)) igindLast++;
Assert (ptxtobj->igindLim == 0 || FBetween (igindLast, ptxtobj->igindFirst, ptxtobj->igindLim-1));
return igindLast;
}
/* =================================================================== */
/* IgindBaseFromIgind: */
/* Returns last glyph with non-zero width before IGIND in this context */
/* */
/* Contact: antons */
/* =================================================================== */
long IgindBaseFromIgind(PILSOBJ pilsobj, long igind)
{
TXTGINF* pginf = pilsobj->pginf;
/* Very simple... just scan back until <> 0 */
while (pilsobj->pdurGind [igind] == 0 && !(pginf [igind] & ginffFirstInContext))
{
Assert (igind > 0);
igind --;
}
return igind;
}
/* =================================================================== */
/* IwchFirstFromIgind: */
/* Returns first IWCH in the context for a given IGIND */
/* */
/* Contact: antons */
/* =================================================================== */
long IwchFirstFromIgind(PTXTOBJ ptxtobj, long igind)
{
PILSOBJ pilsobj = ptxtobj->plnobj->pilsobj;
TXTINF* ptxtinf = pilsobj->ptxtinf;
TXTGINF* pginf = pilsobj->pginf;
long iwchFirst = ptxtobj->iwchFirst;
long igindLast = ptxtobj->igindFirst;
Assert (FBetween (igind, ptxtobj->igindFirst, ptxtobj->igindLim-1));
/* Go ahead until we have found last GIND of the first conext in txtobj */
while (! (pginf [igindLast] & ginffLastInContext)) igindLast++;
/* The following LOOP goes ahead checking context after context /* beginning of txtobj
INVARIANT:
iwchFirst -- First IWCH of the current context
igindLast -- Last GIND of the current context
The second condition is true because of the "while" above
*/
while (igindLast < igind)
{
/* Asserts to check that INVARIANT is true */
Assert (ptxtinf [iwchFirst].fFirstInContext);
Assert (pginf [igindLast] & ginffLastInContext);
/* Move ahead by 1 context... it is easy */
igindLast++;
while (! (pginf [igindLast] & ginffLastInContext)) igindLast++;
while (! (ptxtinf [iwchFirst]. fLastInContext)) iwchFirst++;
iwchFirst++;
};
/* Asserts to check that we have not gone out from txtobj boundaries before reaching igind */
Assert (FBetween (iwchFirst, ptxtobj->iwchFirst, ptxtobj->iwchLim-1));
Assert (FBetween (igindLast, ptxtobj->igindFirst, ptxtobj->igindLim-1));
/* Well, since INVARIANT is true and "igindLast >= igind",
igind should belong to the current context. What we have to return
is just iwchFirst
*/
return iwchFirst;
}
/* =================================================================== */
/* IwchLastFromIwch: */
/* Returns last iwch of context from given iwch */
/* */
/* Contact: antons */
/* =================================================================== */
long IwchLastFromIwch(PTXTOBJ ptxtobj, long iwch)
{
PILSOBJ pilsobj = ptxtobj->plnobj->pilsobj;
TXTINF* ptxtinf = pilsobj->ptxtinf;
Assert(iwch >= ptxtobj->iwchFirst && iwch < ptxtobj->iwchLim);
while (! (ptxtinf [iwch]. fLastInContext))
iwch++;
Assert(iwch >= ptxtobj->iwchFirst && iwch < ptxtobj->iwchLim);
return iwch;
}
/* =================================================================== */
/* IwchPrevLastFromIwch: */
/* Returns last iwch of previous context from given iwch */
/* */
/* Contact: antons */
/* =================================================================== */
long IwchPrevLastFromIwch(PTXTOBJ ptxtobj, long iwch)
{
PILSOBJ pilsobj = ptxtobj->plnobj->pilsobj;
TXTINF* ptxtinf = pilsobj->ptxtinf;
long iwchFirst = ptxtobj->iwchFirst;
iwch--;
Assert(iwch >= ptxtobj->iwchFirst && iwch < ptxtobj->iwchLim);
while (iwch >= iwchFirst && ! (ptxtinf [iwch]. fLastInContext))
iwch--;
return iwch;
}
/* =================================================================== */
/* FIwchOneToOne: */
/* Checks that IWCH belongs to 1:1 context */
/* */
/* Contact: antons */
/* =================================================================== */
BOOL FIwchOneToOne(PILSOBJ pilsobj, long iwch)
{
return pilsobj->ptxtinf [iwch].fOneToOne;
}
/* =================================================================== */
/* FIwchLastInContext: */
/* Checks that IWCH is last in the context */
/* */
/* Contact: antons */
/* =================================================================== */
BOOL FIwchLastInContext(PILSOBJ pilsobj, long iwch)
{
return pilsobj->ptxtinf [iwch].fLastInContext;
}
/* =================================================================== */
/* FIwchFirstInContext: */
/* Checks that IWCH is first in the context */
/* */
/* Contact: antons */
/* =================================================================== */
BOOL FIwchFirstInContext(PILSOBJ pilsobj, long iwch)
{
return pilsobj->ptxtinf [iwch].fFirstInContext;
}
/* =================================================================== */
/* FIgindLastInContext: */
/* Checks that a given GL index is last in the context */
/* */
/* Contact: antons */
/* =================================================================== */
BOOL FIgindLastInContext(PILSOBJ pilsobj, long igind)
{
return pilsobj->pginf [igind] & ginffLastInContext;
}
/* =================================================================== */
/* FIgindFirstInContext: */
/* Checks that a given GL index is first in the context */
/* */
/* Contact: antons */
/* =================================================================== */
BOOL FIgindFirstInContext(PILSOBJ pilsobj, long igind)
{
return pilsobj->pginf [igind] & ginffFirstInContext;
}
/* =================================================================== */
/* DcpAfterContextFromDcp: */
/* For a given DCP (from the beginning of txtobj) it returns DCP after */
/* context bondary */
/* */
/* Function assumes that DCP starts with 1 and means */
/* "number of characters" from the beginning of txtobj. The resulting */
/* DCP (number of characters) will contain the rest of last context in */
/* given DCP. If context was closed then it returns the same DCP */
/* */
/* Contact: antons */
/* =================================================================== */
long DcpAfterContextFromDcp(PTXTOBJ ptxtobj, long dcp)
{
PILSOBJ pilsobj = ptxtobj->plnobj->pilsobj;
TXTINF* ptxtinf = pilsobj->ptxtinf;
/* Translate dcp to iwchLast */
long iwchLast = ptxtobj->iwchFirst + dcp - 1;
/* Here we check that iwchLast "= dcp-1" is correct for a given txtobj */
Assert (FBetween (iwchLast, ptxtobj->iwchFirst, ptxtobj->iwchLim-1));
/* Just scan ahead until context finishes */
while (! ptxtinf [iwchLast].fLastInContext) iwchLast++;
/* Again check that we are in txtobj boundaries */
Assert (FBetween (iwchLast, ptxtobj->iwchFirst, ptxtobj->iwchLim-1));
/* Translate iwchLast back to dcp */
return iwchLast - ptxtobj->iwchFirst + 1;
}
/* =================================================================== */
/* InterpretMap */
/* */
/* Fills internal CH- and GL- based bits with context information */
/* (the information is used by the rest functions in this file only) */
/* */
/* IN: pilsobj */
/* iwchFirst - The first iwch in "shaped together" chunk */
/* dwch - Number of characters in this chunk */
/* igindFirst - The first gind in "shaped together chunk */
/* cgind - Number of glyphs in this chunk */
/* */
/* OUT: (nothing) */
/* */
/* Contact: antons */
/* =================================================================== */
void InterpretMap(PLNOBJ plnobj, long iwchFirst, long dwch, long igindFirst, long cgind)
{
TXTINF* ptxtinf = plnobj->pilsobj->ptxtinf;
TXTGINF* pginf = plnobj->pilsobj->pginf;
GMAP* pgmap = plnobj->pgmap;
/* Last possible iwch and gind (remember, they are "last", not "lim" */
long iwchLast = iwchFirst + dwch - 1;
long igindLast = igindFirst + cgind - 1;
/* Two global variables for main loop */
long iwchFirstInContext = iwchFirst;
long igindFirstInContext = igindFirst;
/* The following WHILE translates context after context
INVARIANT:
* iwchFirstInContext -- The first iwch in current context
* igindFirstInContext -- The first gind in current context
* All context to the left from current have been translated
The loop translates current context and moves iwchFirstIn... &
igindFirst... to the next context
*/
while (iwchFirstInContext <= iwchLast)
/* According to D.Gris I should have checked "!= iwchLast+1" but I do not
like ship version to come to infinite loop even because of wrong data ;-)
For debug, I will have Assert right after loop terminates */
{
/* Variables for last gind and iwch of the current context */
long igindLastInContext;
long iwchLastInContext = iwchFirstInContext;
/* Just to make sure that igindFirst... corresponds to iwchFirst... */
Assert ( pgmap [iwchFirstInContext] + igindFirst == igindFirstInContext );
Assert (iwchLastInContext <= iwchLast);
/* P.S. Since pgmap values are RELATIVE to the beginning of "shape together"
chunk, we shall ALWAYS add igindFirst to pgmap value in order to get
GL index in our meaning
*/
/* Following simple loop with find correct iwchLastInContext */
/* Note that we add igindFirst to pgmap value (see PS. above) */
while ((iwchLastInContext <= iwchLast) && (pgmap [iwchLastInContext] + igindFirst == igindFirstInContext))
iwchLastInContext++;
iwchLastInContext--;
/* Now we know iwchLastInContextare and we are ready to find igindLastInContext
I will peep in pgmap value of the character following iwchLastInContext or take
last avaiable GL index (igindLast) if iwchLastInContext is really last available
*/
igindLastInContext = (iwchLastInContext < iwchLast ?
pgmap [iwchLastInContext+1] + igindFirst - 1 :
igindLast
);
/* Check that there is at least one GL inside our context */
/* Note: we do not need to check the same for characters */
Assert (igindFirstInContext <= igindLastInContext);
/* It is time to set flags in our GL and CH arrays */
if ( ( iwchFirstInContext == iwchLastInContext) &&
(igindFirstInContext == igindLastInContext))
{
/* We have 1:1 mapping (I separate it for better perfomance) */
ptxtinf [iwchFirstInContext].fOneToOne = fTrue;
ptxtinf [iwchFirstInContext].fFirstInContext = fTrue;
ptxtinf [iwchFirstInContext].fLastInContext = fTrue;
/* See comments in "General case" */
pginf [igindFirstInContext] |= ginffOneToOne | ginffFirstInContext | ginffLastInContext;
}
else
{
/* General case when there is not 1:1 mapping */
long i; /* Variable for two loops */
/* Set up character-based bits */
for (i=iwchFirstInContext; i<=iwchLastInContext; i++)
{
ptxtinf [i].fOneToOne = fFalse; /* Of course, it is not 1:1 */
/* I was considering whether to place boundary cases (first/last character
in context) outside loop but finally came to the conclusion that it would
cheaper both for code and perfomance to check it for each character as
follows */
ptxtinf [i].fFirstInContext = (i==iwchFirstInContext);
ptxtinf [i].fLastInContext = (i==iwchLastInContext);
};
/* With glyph-based flags we can win some perfomance by setting all bits in
one operation (since they are really bits, not booleans. Again I do not like
to do separate job for context boundaries */
for (i=igindFirstInContext; i<=igindLastInContext; i++)
pginf [i] &= ~ (ginffOneToOne | ginffFirstInContext |
ginffLastInContext);
/* And finally I set corresponding bits for the first & last GLs in the context */
pginf [igindFirstInContext] |= ginffFirstInContext;
pginf [igindLastInContext] |= ginffLastInContext;
};
/* To start loop again we have to move to the next context. Now it is easy... */
iwchFirstInContext = iwchLastInContext+1;
igindFirstInContext = igindLastInContext+1;
};
/* See comments in the beginning of the loop */
Assert (iwchFirstInContext == iwchLast + 1);
Assert (igindFirstInContext == igindLast + 1);
/* And according to INVARIANT, we are done */
}