521 lines
16 KiB
C
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 */
|
||
|
}
|