#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 */ }