{******************************************************************************}
{*                                                                            *}
{*  Copyright (c) Microsoft Corporation. All rights reserved.                 *}
{*                                                                            *}
{*  File:       usp10.h                                                       *}
{*  Content:    USP - Unicode Complex Script processor                        *}
{*                                                                            *}
{*  Delphi / FreePascal adaptation by Alexey Barkovoy (clootie@clootie.ru)    *}
(*                                                                            *)
{*  The original version from Alexey Barkovoy can be downloaded from:         *}
{*     http://clootie.ru                                                      *}
(*                                                                            *)
(*  Dynamic linking logic (similar to what the JCL does) by Maël Hörz.        *)
(*                                                                            *)
(*  Latest version can be downloaded from http://mh-nexus.de/unisynedit.htm   *)
(*  or checked out from the Unicode branch of SynEdit CVS.                    *) 
(*                                                                            *)
{******************************************************************************}
{                                                                              }
{ The contents of this file are used with permission, subject to the Mozilla   }
{ Public License Version 1.1 (the "License"); you may not use this file except }
{ in compliance with the License. You may obtain a copy of the License at      }
{ http://www.mozilla.org/MPL/MPL-1.1.html                                      }
{                                                                              }
{ Software distributed under the License is distributed on an "AS IS" basis,   }
{ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for }
{ the specific language governing rights and limitations under the License.    }
{                                                                              }
{ Alternatively, the contents of this file may be used under the terms of the  }
{ GNU Lesser General Public License (the  "LGPL License"), in which case the   }
{ provisions of the LGPL License are applicable instead of those above.        }
{ If you wish to allow use of your version of this file only under the terms   }
{ of the LGPL License and not to allow others to use your version of this file }
{ under the MPL, indicate your decision by deleting  the provisions above and  }
{ replace  them with the notice and other provisions required by the LGPL      }
{ License.  If you do not delete the provisions above, a recipient may use     }
{ your version of this file under either the MPL or the LGPL License.          }
{                                                                              }
{ For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html }
{                                                                              }
{******************************************************************************}

// $Id: SynUsp10.pas,v 1.1.2.2 2008/09/17 13:59:12 maelh Exp $

// necessary for dynamic linking
{$STACKFRAMES ON}
{$WARNINGS OFF}

unit SynUsp10;

interface

uses
  Windows;

const
  ///// Uniscribe build number
  USPBUILD = 0400;



  /////   USP - Unicode Complex Script processor
  //
  //      Copyright (c) Microsoft Corporation. All rights reserved.




  /////   SCRIPT
  //
  //      The SCRIPT enum is an opaque type used internally to identify
  //      which shaping engine functions are used to process a given run.
  //
  //
  SCRIPT_UNDEFINED  = 0;
  //
  //p     SCRIPT_UNDEFINED: This is the only public script ordinal. May be
  //      forced into the eScript field of a SCRIPT_ANALYSIS to disable shaping.
  //      SCRIPT_UNDEFINED is supported by all fonts - ScriptShape will display
  //      whatever glyph is defined in the font CMAP table, or, if none, the
  //      missing glyph.







  /////   USP Status Codes
  //
  USP_E_SCRIPT_NOT_IN_FONT   = DWord((SEVERITY_ERROR shl 31) or (FACILITY_ITF shl 16)) or $200; // MAKE_HRESULT(SEVERITY_ERROR,FACILITY_ITF,0x200)    // Script doesn't exist in font






  /////   SCRIPT_CACHE
  //
  //      Many script APIs take a combination of HDC and SCRIPT_CACHE parameter.
  //
  //      A SCRIPT_CACHE is an opaque pointer to a Uniscribe font metric cache
  //      structure.

type
  SCRIPT_CACHE = Pointer;
  {$EXTERNALSYM SCRIPT_CACHE}
  TScriptCache = SCRIPT_CACHE;
  PScriptCache = ^TScriptCache;


//      The client must allocate and retain one SCRIPT_CACHE variable for each
//      character style used. It must be initialised by the client to NULL.
//
//      APIs are passed an HDC and the address of a SCRIPT_CACHE variable.
//      Uniscribe will first attempt to access font data via the SCRIPT_CACHE
//      and will only inspect the HDC if the required data is not already
//      cached.
//
//      The HDC may be passed as NULL. If data required by Uniscribe is
//      already cached, the HDC won't be accessed and operation continues
//      normally.
//
//      If the HDC is passed as NULL, and Uniscribe needs to access it for
//      any reason, Uniscribe will return E_PENDING.
//
//      E_PENDING is returned quickly, allowing the client to avoid time
//      consuming SelectObject calls. The following example applies to all
//      APIs that take a SCRIPT_CACHE and an optional HDC.
//
//c     hr = ScriptShape(NULL, &sc, ..);
//c     if (hr == E_PENDING) {
//c         ... select font into hdc ...
//c         hr = ScriptShape(hdc, &sc, ...);
//c     }






/////   ScriptFreeCache
//
//      The client may free a SCRIPT_CACHE at any time. Uniscribe maintains
//      reference counts in it's font and shaper caches, and frees font data
//      only when all sizes of the font are free, and shaper data only when
//      all fonts it supports are freed.
//
//      The client should free the SCRIPT_CACHE for a style when it discards
//      that style.
//
//      ScriptFreeCache always sets it's parameter to NULL to help avoid
//      mis-referencing.


function ScriptFreeCache(
    psc: PScriptCache //InOut  Cache handle
 ): HRESULT; stdcall;






type
  /////   SCRIPT_CONTROL
  //
  //      The SCRIPT_CONTROL structure provides itemization control flags to the
  //      ScriptItemize function.
  //
  //

  TScriptControl_enum = (
    fContextDigits,       // Means use previous script instead of uDefaultLanguage
    // The following flags provide legacy support for GetCharacterPlacement features
    fInvertPreBoundDir,   // Reading order of virtual item immediately prior to string
    fInvertPostBoundDir,  // Reading order of virtual item immediately following string
    fLinkStringBefore,    // Equivalent to presence of ZWJ before string
    fLinkStringAfter,     // Equivalent to presence of ZWJ after string
    fNeutralOverride,     // Causes all neutrals to be strong in the current embedding direction
    fNumericOverride,     // Causes all numerals to be strong in the current embedding direction
    fLegacyBidiClass      // Causes plus and minus to be reated as neutrals, slash as a common separator
  );
  TScriptControl_set = set of TScriptControl_enum;

  PScriptControl = ^TScriptControl;
  tag_SCRIPT_CONTROL = packed record
    uDefaultLanguage: Word;     // For NADS, also default for context
    fFlags: TScriptControl_set;
    fReserved: Byte;
  end;
(*  uDefaultLanguage: DWORD    {:16}; // For NADS, also default for context
    fContextDigits: DWORD      {:1};  // Means use previous script instead of uDefaultLanguage

    // The following flags provide legacy support for GetCharacterPlacement features
    fInvertPreBoundDir: DWORD  {:1};  // Reading order of virtual item immediately prior to string
    fInvertPostBoundDir: DWORD {:1};  // Reading order of virtual item immediately following string
    fLinkStringBefore: DWORD   {:1};  // Equivalent to presence of ZWJ before string
    fLinkStringAfter: DWORD    {:1};  // Equivalent to presence of ZWJ after string
    fNeutralOverride: DWORD    {:1};  // Causes all neutrals to be strong in the current embedding direction
    fNumericOverride: DWORD    {:1};  // Causes all numerals to be strong in the current embedding direction
    fLegacyBidiClass: DWORD    {:1};  // Causes plus and minus to be reated as neutrals, slash as a common separator
    fReserved: DWORD           {:8};
  end; *)
  {$EXTERNALSYM tag_SCRIPT_CONTROL}
  SCRIPT_CONTROL = tag_SCRIPT_CONTROL;
  {$EXTERNALSYM SCRIPT_CONTROL}
  TScriptControl = tag_SCRIPT_CONTROL;

//
//
//p     uDefaultLanguage: Language to use when Unicode values are ambiguous.
//              Used by numeric processing to select digit shape when
//              fDigitSubstitute (see SCRIPT_STATE) is in force.
//
//p     fContextDigits: Specifies that national digits are chosen according to
//              the nearest previous strong text, rather than using
//              uDefaultLanguage.
//
//p     fInvertPreBoundDir: By default text at the start of the string is
//              laid out as if it follows strong text of the same direction
//              as the base embedding level. Set fInvertPreBoundDir to change
//              the initial context to the opposite of the base embedding
//              level. This flag is for GetCharacterPlacement legacy support.
//
//p     fInvertPostBoundDir: By default text at the end of the string is
//              laid out as if it preceeds strong text of the same direction
//              as the base embedding level. Set fInvertPostBoundDir to change
//              the final context to the opposite of the base embedding
//              level. This flag is for GetCharacterPlacement legacy support.
//
//p     fLinkStringBefore: Causes the first character of the string to be
//              shaped as if were joined to a previous character.
//
//p     fLinkStringAfter: Causes the last character of the string to be
//              shaped as if were joined to a following character.
//
//p     fNeutralOverride: Causes all neutral characters in the string to be
//              treated as if they were strong characters of their enclosing
//              embedding level. This effectively locks neutrals in place,
//              reordering occuring only between neutrals.
//
//p     fNumericOverride: Causes all numeric characters in the string to be
//              treated as if they were strong characters of their enclosing
//              embedding level. This effectively locks numerics in place,
//              reordering occuring only between numerics.
//
//p     fReserved: Reserved. Always initialise to 0.






  /////   SCRIPT_STATE
  //
  //      The SCRIPT_STATE structure is used both to initialise the unicode
  //      algorithm state as an input parameter to ScriptItemize, and is also
  //      a component of each item analysis returned by ScriptItemize.
  //
  //
  TScriptState_enum = (
    uBidiLevel_reserved1, uBidiLevel_r2, uBidiLevel_r3, uBidiLevel_r4, uBidiLevel_r5,
    fOverrideDirection, // Set when in LRO/RLO embedding
    fInhibitSymSwap,    // Set by U+206A (ISS), cleared by U+206B (ASS)
    fCharShape,         // Set by U+206D (AAFS), cleared by U+206C (IAFS)
    fDigitSubstitute,   // Set by U+206E (NADS), cleared by U+206F (NODS)
    fInhibitLigate,     // Equiv !GCP_Ligate, no Unicode control chars yet
    fDisplayZWG,        // Equiv GCP_DisplayZWG, no Unicode control characters yet
    fArabicNumContext,  // For EN->AN Unicode rule
    fGcpClusters        // For Generating Backward Compatible GCP Clusters (legacy Apps)
  );
  TScriptState_set = set of TScriptState_enum;

  PScriptState = ^TScriptState;
  tag_SCRIPT_STATE = packed record
   case Byte of
    0: (uBidiLevel: Byte)    {:5};  // Unicode Bidi algorithm embedding level (0-16)
    1: (fFlags: TScriptState_set)
  end;
(*  uBidiLevel: Word         {:5};  // Unicode Bidi algorithm embedding level (0-16)
    fOverrideDirection: Word {:1};  // Set when in LRO/RLO embedding
    fInhibitSymSwap: Word    {:1};  // Set by U+206A (ISS), cleared by U+206B (ASS)
    fCharShape: Word         {:1};  // Set by U+206D (AAFS), cleared by U+206C (IAFS)
    fDigitSubstitute: Word   {:1};  // Set by U+206E (NADS), cleared by U+206F (NODS)
    fInhibitLigate: Word     {:1};  // Equiv !GCP_Ligate, no Unicode control chars yet
    fDisplayZWG: Word        {:1};  // Equiv GCP_DisplayZWG, no Unicode control characters yet
    fArabicNumContext: Word  {:1};  // For EN->AN Unicode rule
    fGcpClusters: Word       {:1};  // For Generating Backward Compatible GCP Clusters (legacy Apps)
    fReserved: Word          {:1};
    fEngineReserved: Word    {:2};  // For use by shaping engine
  end; *)
  {$EXTERNALSYM tag_SCRIPT_STATE}
  SCRIPT_STATE = tag_SCRIPT_STATE;
  {$EXTERNALSYM SCRIPT_STATE}
  TScriptState = tag_SCRIPT_STATE;

const
  MASK_uBidiLevel = $1F; // Mask to apply to TScriptState.uBidiLevel
type

//
//
//p     uBidiLevel: The embedding level associated with all characters in this
//              run according to the Unicode bidi algorithm. When passed to
//              ScriptItemize, should be initialised to 0 for an LTR base
//              embedding level, or 1 for RTL.
//
//p     fOverrideDirection: TRUE if this level is an override level (LRO/RLO).
//              In an override level, characters are layed out purely
//              left to right, or purely right to left. No reordering of digits
//              or strong characters of opposing direction takes place.
//              Note that this initial value is reset by LRE, RLE, LRO or
//              RLO codes in the string.
//
//p     fInhibitSymSwap: TRUE if the shaping engine is to bypass mirroring of
//              Unicode Mirrored glyphs such as brackets. Set by Unicode
//              character ISS, cleared by ASS.
//
//p     fCharShape: TRUE if character codes in the Arabic Presentation Forms
//              areas of Unicode should be shaped. (Not implemented).
//
//p     fDigitSubstitute: TRUE if character codes U+0030 through U+0039
//              (European digits) are to be substituted by national digits.
//              Set by Unicode NADS, Cleared by NODS.
//
//p     fInhibitLigate: TRUE if ligatures are not to be used in the shaping
//              of Arabic or Hebrew characters.
//
//p     fDisplayZWG: TRUE if control characters are to be shaped as
//              representational glyphs. (Normally, control characters are
//              shaped to the blank glyph and given a width of zero).
//
//p     fArabicNumContext: TRUE indicates prior strong characters were Arabic
//              for the purposes of rule P0 on page 3-19 of 'The Unicode
//              Standard, version 2.0'. Should normally be set TRUE before
//              itemizing an RTL paragraph in an Arabic language, FALSE
//              otherwise.
//
//p     fGcpClusters: For GetCharaterPlacement legacy support only.
//              Initialise to TRUE to request ScriptShape to generate
//              the LogClust array the same way as GetCharacterPlacement
//              does in Arabic and Hebrew Windows95. Affects only Arabic
//              and Hebrew items.
//
//p     fReserved: Reserved. Always initialise to 0.
//
//p     fEngineReserved: Reserved. Always initialise to 0.






  /////   SCRIPT_ANALYSIS
  //
  //      Each analysed item is described by a SCRIPT_ANALYSIS structure.
  //      It also includes a copy of the Unicode algorithm state (SCRIPT_STATE).
  //
  //
  TScriptAnalysis_enum = (
    eScript_r1, eScript_r2, eScript_r3, eScript_r4, eScript_r5,  // first 10 bits
    eScript_r6, eScript_r7, eScript_r8, eScript_r9, eScript_r10, // are reserved
    fRTL,             // Rendering direction
    fLayoutRTL,       // Set for GCP classes ARABIC/HEBREW and LOCALNUMBER
    fLinkBefore,      // Implies there was a ZWJ before this item
    fLinkAfter,       // Implies there is a ZWJ following this item.
    fLogicalOrder,    // Set by client as input to ScriptShape/Place
    fNoGlyphIndex     // Generated by ScriptShape/Place - this item does not use glyph indices
  );
  TScriptAnalysis_set = set of TScriptAnalysis_enum;

  PScriptAnalysis = ^TScriptAnalysis;
  tag_SCRIPT_ANALYSIS = packed record
   case Byte of
    0: (eScript: Word)   {:10};    // Shaping engine
    1: (fFlags: TScriptAnalysis_set;
        s: TScriptState)
  end;
(*  eScript: Word         {:10};    // Shaping engine
    fRTL: Word            {:1};     // Rendering direction
    fLayoutRTL: Word      {:1};     // Set for GCP classes ARABIC/HEBREW and LOCALNUMBER
    fLinkBefore: Word     {:1};     // Implies there was a ZWJ before this item
    fLinkAfter: Word      {:1};     // Implies there is a ZWJ following this item.
    fLogicalOrder: Word   {:1};     // Set by client as input to ScriptShape/Place
    fNoGlyphIndex: Word   {:1};     // Generated by ScriptShape/Place - this item does not use glyph indices
    s: TScriptState;
  end; *)
  {$EXTERNALSYM tag_SCRIPT_ANALYSIS}
  SCRIPT_ANALYSIS = tag_SCRIPT_ANALYSIS;
  {$EXTERNALSYM SCRIPT_ANALYSIS}
  TScriptAnalysis = tag_SCRIPT_ANALYSIS;

const
  MASK_eScript = $3FF; // Mask to apply to TScriptAnalysis.eScript
type

//
//
//p     eScript: Opaque value identifying which engine Uniscribe will use to
//              Shape, Place and TextOut this item. The value of eScript is
//              undefined, and will change in future releases, but attributes
//              of eScript may be obtained by calling ScriptGetProperties.
//
//p     fRTL: Rendering direction. Normally identical to the parity of the
//              Unicode embedding level, but may differ if overridden by
//              GetCharacterPlacement legacy support.
//
//p     fLayoutRTL: Logical direction - whether conceptually part of a
//              left-to-right sequenece or a right-to-left sequence. Although
//              this is usually the same as fRTL, for a number in a
//              right-to-left run, fRTL is False (because digits are always
//              displayed LTR), but fLayoutRTL is True (because the number is
//              read as part of the right-to-left sequence).
//
//p     fLinkBefore: If set, the shaping engine will shape the first character
//              of this item as if it were joining with a previous character.
//              Set by ScriptItemize, may be overriden before calling ScriptShape.
//
//p     fLinkAfter: If set, the shaping engine will shape the last character
//              of this item as if it were joining with a subsequient character.
//              Set by ScriptItemize, may be overriden before calling ScriptShape.
//
//p     fLogicalOrder: If set, the shaping engine will generate all glyph
//              related arrays in logical order. By default glyph related
//              arrays are in visual order, the first array entry corresponding
//              to the leftmost glyph.
//              Set to FALSE by ScriptItemize, may be overriden before calling
//              ScriptShape.
//
//p     fNoGlyphIndex: May be set TRUE on input to ScriptShape to disable use
//              of glyphs for this item. Additionally, ScriptShape will set it
//              TRUE for hdcs containing symbolic, unrecognised and device fonts.
//              Disabling glyphing disables complex script shaping. When set,
//              shaping and placing for this item is implemented directly by
//              calls to GetTextExtentExPoint and ExtTextOut.
/////   SCRIPT_ITEM
//
//      The SCRIPT_ITEM structure includes a SCRIPT_ANALYSIS with the string
//      ofset of the first character of the item.
//
//

  PScriptItem = ^TScriptItem;
  tag_SCRIPT_ITEM = record
    iCharPos: Integer;      // Logical offset to first character in this item
    a: TScriptAnalysis;
  end;
  {$EXTERNALSYM tag_SCRIPT_ITEM}
  SCRIPT_ITEM = tag_SCRIPT_ITEM;
  {$EXTERNALSYM SCRIPT_ITEM}
  TScriptItem = SCRIPT_ITEM;

//
//
//p     iCharPos: Offset from beginning of itemised string to first character
//              of this item, counted in Unicode codepoints (i.e. words).
//
//p     a: Script analysis structure containing analysis specific to this
//              item, to be passed to ScriptShape, ScriptPlace etc.






/////   ScriptItemize - break text into items
//
//      Breaks a run of unicode into individually shapeable items.
//      Items are delimited by
//
//      o Change of shaping engine
//      o Change of direction
//
//      The client may create multiple runs from each item returned by
//      ScriptItemize, but should not combine multiple items into a single run.
//
//      Later the client will call ScriptShape for each run (when measuring or
//      rendering), and must pass the SCRIPT_ANALYSIS that ScriptItemize
//      returned.


function ScriptItemize(
    const pwcInChars: PWideChar;     // In   Unicode string to be itemized
    cInChars: Integer;               // In   Codepoint count to itemize
    cMaxItems: Integer;              // In   Max length of itemization array
    const psControl: PScriptControl; // In   Analysis control (optional)
    const psState: PScriptState;     // In   Initial bidi algorithm state (optional)
    pItems: PScriptItem;             // Out  Array to receive itemization
    pcItems: PInteger                // Out  Count of items processed (optional)
 ): HRESULT; stdcall;
{$EXTERNALSYM ScriptItemize}






/////
//
//
//      Returns E_INVALIDARG if pwcInChars == NULL or cInChars == 0
//          or pItems == NULL or cMaxItems < 2.
//
//      Returns E_OUTOFMEMORY if the output buffer length (cMaxItems) is
//          insufficient. Note that in this case, as in all error cases, no
//          items have been fully processed so no part of the output array
//          contains defined values.
//
//      If psControl and psState are NULL on entry, ScriptItemize
//      breaks the unicode string purely by character code.  If they are all
//      non-null, it performs a full Unicode bidi analysis.
//
//      ScriptItemize always adds a terminal item to the item analysis array
//      (pItems) such that the length of an item at pItem is always available as:
//
//c     pItem[1].iCharPos - pItem[0].iCharPos
//
//      For this reason, it is invalid to call ScriptItemize with a buffer
//      of less than two SCRIPT_ANALYSIS items.
//
//      To perform a correct Unicode Bidi analysis, the SCRIPT_STATE should
//      be initialised according to the paragraph reading order at paragraph
//      start, and ScriptItemize should be passed the whole paragraph.
//
//      fRTL and fNumeric together provide the same classification as
//      the lpClass output from GetCharacterPlacement.
//
//      European digits U+0030 through U+0039 may be rendered as national
//      digits as follows:
//
//t     fDigitSubstitute | FContextDigits | Digit shapes displayed for Unicode U+0030 through U+0039
//t     ---------------- | -------------- | ------------------------------------
//t     False            | Any            | Western (European / American) digits
//t     True             | False          | As specified in SCRIPT_CONTROL.uDefaultLanguage
//t     True             | True           | As prior strong text, defaulting to SCRIPT_CONTROL.uDefaultLanguage
//
//
//      For fContextDigits, any Western digits (U+0030 - U+0039) encountered
//      before the first strongly directed character are substituted by the
//      traditional digits of the SCRIPT_CONTROL.uDefaultLanguage when that
//      language is written in the same direction as SCRIPT_STATE.uBidiLevel.
//
//      Thus, in a right-to-left string, if SCRIPT_CONTROL.uDefaultLanguage is
//      1 (LANG_ARABIC), then leading Western digits will be substituted by
//      traditional Arabic digits.
//
//      However, also in a right-to-left string, if SCRIPT_CONTROL.uDefaultLanguage
//      is 0x1e (LANG_THAI), then no substitution occurs on leading Western
//      digits because the Thai language is written left-to-right.
//
//      Following strongly directed characters, digits are substituted
//      by the traditional digits associated with the closest prior strongly
//      directed character.
//
//      The left-to-right mark (LRM) and right-to-left mark (RLM) are strong
//      characters whose language depends on the SCRIPT_CONTROL.uDefaultLangauge.
//
//      If SCRIPT_CONTROL.uDefaultLangauge is a left-to-right langauge, then
//      LRM causes subsequent Western digits to be substituted by the
//      traditional digits associated with that language, while Western
//      digits following RLM are not substituted.
//
//      Conversly, if SCRIPT_CONTROL.uDefaultLangauge is a right-to-left
//      langauge, then Western digits following LRM are not substituted, while
//      Western digits following RLM are substituted by the traditional digits
//      associated with that language.
//
//
//
//      Effect of Unicode control characters on SCRIPT_STATE:
//
//t     SCRIPT_STATE flag | Set by | Cleared by
//t     ----------------- | ------   ----------
//t     fDigitSubstitute  |  NADS  |   NODS
//t     fInhibitSymSwap   |  ISS   |   ASS
//t     fCharShape        |  AAFS  |   IAFS
//
//      SCRIPT_STATE.fArabicNumContext controls the Unicode EN->AN rule.
//      It should normally be initialised to TRUE
//      before itemizing an RTL paragraph in an Arabic language, FALSE
//      otherwise.
/////   ScriptLayout
//
//      The ScriptLayout function converts an array of run embedding levels to
//      a map of visual to logical position, and/or logical to visual position.
//
//      pbLevel must contain the embedding levels for all runs on the line,
//      ordered logically.
//
//      On output, piVisualToLogical[0] is the logical index of the run to
//      display at the far left. Subsequent entries should be displayed
//      progressing from left to right.
//
//      piLogicalToVisual[0] is the relative visual position where the first
//      logical run should be displayed - the leftmost display position being zero.
//
//      The caller may request either piLogicalToVisual or piVisualToLogical
//      or both.
//
//      Note: No other input is required since the embedding levels give all
//      necessary information for layout.


function ScriptLayout(
    cRuns: Integer;               // In   Number of runs to process
    const pbLevel: PByte;         // In   Array of run embedding levels
    piVisualToLogical: PInteger;  // Out  List of run indices in visual order
    piLogicalToVisual: PInteger   // Out  List of visual run positions
 ): HRESULT; stdcall;
{$EXTERNALSYM ScriptLayout}





type

  /////   SCRIPT_JUSTIFY
  //
  //      The script justification enumeration provides the client with the
  //      glyph characteristic information it needs to implement justification.

  PScriptJustify = ^TScriptJustify;
  tag_SCRIPT_JUSTIFY = (
    SCRIPT_JUSTIFY_NONE           {= 0},   // Justification can't be applied at this glyph
    SCRIPT_JUSTIFY_ARABIC_BLANK   {= 1},   // This glyph represents a blank in an Arabic run
    SCRIPT_JUSTIFY_CHARACTER      {= 2},   // Inter-character justification point follows this glyph
    SCRIPT_JUSTIFY_RESERVED1      {= 3},   // Reserved #1
    SCRIPT_JUSTIFY_BLANK          {= 4},   // This glyph represents a blank outside an Arabic run
    SCRIPT_JUSTIFY_RESERVED2      {= 5},   // Reserved #2
    SCRIPT_JUSTIFY_RESERVED3      {= 6},   // Reserved #3
    SCRIPT_JUSTIFY_ARABIC_NORMAL  {= 7},   // Normal Middle-Of-Word glyph that connects to the right (begin)
    SCRIPT_JUSTIFY_ARABIC_KASHIDA {= 8},   // Kashida(U+640) in middle of word
    SCRIPT_JUSTIFY_ARABIC_ALEF    {= 9},   // Final form of Alef-like (U+627, U+625, U+623, U+632)
    SCRIPT_JUSTIFY_ARABIC_HA      {= 10},  // Final form of Ha (U+647)
    SCRIPT_JUSTIFY_ARABIC_RA      {= 11},  // Final form of Ra (U+631)
    SCRIPT_JUSTIFY_ARABIC_BA      {= 12},  // Middle-Of-Word form of Ba (U+628)
    SCRIPT_JUSTIFY_ARABIC_BARA    {= 13},  // Ligature of alike (U+628,U+631)
    SCRIPT_JUSTIFY_ARABIC_SEEN    {= 14},  // Highest priority: Initial shape of Seen(U+633) (end)
    SCRIPT_JUSTIFY_RESERVED4      {= 15}   // Reserved #4
  );
  SCRIPT_JUSTIFY = tag_SCRIPT_JUSTIFY;
  {$EXTERNALSYM SCRIPT_JUSTIFY}
  TScriptJustify = SCRIPT_JUSTIFY;



  /////   SCRIPT_VISATTR
  //
  //      The visual (glyph) attribute buffer generated by ScriptShape
  //      identifies clusters and justification points:

  TScriptVisAttr_enum = (
    uJustification_r1, uJustification_r2, uJustification_r3, uJustification_r4,
    fClusterStart,     {:1}  // First glyph of representation of cluster
    fDiacritic,        {:1}  // Diacritic
    fZeroWidth,        {:1}  // Blank, ZWJ, ZWNJ etc, with no width
    fReserved          {:1}  // General reserved
  );
  TScriptVisAttr_set = set of TScriptVisAttr_enum;

  PScriptVisAttr = ^TScriptVisAttr;
  tag_SCRIPT_VISATTR = packed record
   case Byte of
    0: (uJustification: Byte) {:4};  // Justification class
    1: (fFlags: TScriptVisAttr_set;
        fShapeReserved: Byte) {:8};  // Reserved for use by shaping engines
  end;
(*  uJustification: Word   {:4};  // Justification class
    fClusterStart: Word    {:1};  // First glyph of representation of cluster
    fDiacritic: Word       {:1};  // Diacritic
    fZeroWidth: Word       {:1};  // Blank, ZWJ, ZWNJ etc, with no width
    fReserved: Word        {:1};  // General reserved
    fShapeReserved: Word   {:8};  // Reserved for use by shaping engines
  end; *)
  SCRIPT_VISATTR = tag_SCRIPT_VISATTR;
  {$EXTERNALSYM SCRIPT_VISATTR}
  TScriptVisAttr = SCRIPT_VISATTR;

const
  MASK_uJustification = $F; // Mask to apply to TScriptVisAttr.uJustification

//
//
//p     uJustification: Justification class for this glyph. See SCRIPT_JUSTIFY.
//
//p     fClusterStart: Set for the logically first glyph in every cluster,
//          even for clusters containing just one glyph.
//
//p     fDiacritic: Set for glyphs that combine with base characters.
//
//p     fZeroWidth: Set by the shaping engine for some, but not all, zero
//          width characters.


/////   ScriptShape
//
//      The ScriptShape function takes a Unicode run and generates glyphs and
//      visual attributes.
//
//      The number of glyphs generated varies according to the script and the
//      font. Only for simple scripts and fonts does each Unicode code point
//      generates a single glyph.
//
//      There is no limit on the number of glyphs generated by a codepoint.
//      For example, a sophisticated complex script font might choose to
//      constuct characters from components, and so generate many times as
//      many glyphs as characters.
//
//      There are also special cases like invalid character representations,
//      where extra glyphs are added to represent the invalid sequence.
//
//      A reasonable guess might be to provide a glyph buffer 1.5 times the
//      length of the character buffer, plus a 16 glyph fixed addition for
//      rare cases like invalid sequenece representation.
//
//      If ScriptShape returns E_OUTOFMEMORY it will be necessary to recall
//      it, possibly more than once, until a large enough buffer is found.


function ScriptShape(
    hdc: HDC;                    // In    Optional (see under caching)
    psc: PScriptCache;           // InOut Cache handle
    const pwcChars: PWideChar;   // In    Logical unicode run
    cChars: Integer;             // In    Length of unicode run
    cMaxGlyphs: Integer;         // In    Max glyphs to generate
    psa: PScriptAnalysis;        // InOut Result of ScriptItemize (may have fNoGlyphIndex set)
    pwOutGlyphs: PWord;          // Out   Output glyph buffer
    pwLogClust: PWord;           // Out   Logical clusters
    psva: PScriptVisAttr;        // Out   Visual glyph attributes
    pcGlyphs: PInteger           // Out   Count of glyphs generated
 ): HRESULT; stdcall;
{$EXTERNALSYM ScriptShape}






/////
//
//      Returns E_OUTOFMEMORY if the output buffer length (cMaxGlyphs) is
//          insufficient. Note that in this case, as in all error cases, the
//          content of all output parameters are undefined.
//
//p     psa: Pass the SCRIPT_ANALYSIS field of the SCRIPT_ITEM entry for this
//          item. (The SCRIPT_ITEM array is returned by ScriptItemize.)
//
//      Clusters are sequenced uniformly within the run, as are glyphs within
//      the cluster - the fRTL item flag (from ScriptItemize) identifies
//      whether left to right, or right to left.
//
//p     pwLogClust: has cChars elements - each entry in pwLogClust corresponds
//          to a character in the input string (pwcChars). The value in each
//          pwLogCLust entry is the offset of the first glyph in the cluster
//          that contains this character.
//
//      Example: In the following example, there are four clusters:
//      1st cluster: one character represented by one glyph
//      2nd cluster: one character represented by 3 glyphs
//      3rd cluster: three characters represented by one glyph
//      4th cluster: 2 characters represented by three glyphs
//
//      Glyph array: (c<n>g<m> means cluster n glyph m)
//c        0      1    2    3      4      5    6    7
//c     -------------------------------------------------
//c     | c1g1 | c2g1 c2g2 c2g3 | c3g1 | c4g1 c4g2 c4g3 |
//c     -------------------------------------------------
//
//      Character array: (c<n>u<m> means cluster n Unicode codepoint m)
//c        0      1      2    3    4      5    6
//c     --------------------------------------------
//c     | c1u1 | c2u1 | c3u1 c3u2 c3u3 | c4u1 c4u2 |
//c     --------------------------------------------
//
//      LogClust: (one entry per character gives 1st glyph in cluster
//c     --------------------------------------------
//c     |   0  |   1  |   4    4    4  |   5    5  |
//c     --------------------------------------------
//
//      Note that for an RTL run (SCRIPT_ANALYSIS.a.fRTL == TRUE) and when
//      fLogicalOrder == FALSE (the default), glyphs are generated in visual
//      order - the reverse of the codepoint order, and the values in the
//      LogClust array will be descending.
//
//
//p     psva: has one visual attribute per glyph and so has maxGlyphs entries.
//
//
//      ScriptShape may set the fNoGlyphIndex flag in psa if the font or
//      OS cannot support glyph indices.
//
//      If fLogicalOrder is requested in psa, glyphs will be always be
//      generated in the same order as the original Unicode characters.
//
//      If fLogicalOrder is not set, right to left items are generated in
//      reverse order, so ScriptTextOut does not need to reverse them before
//      calling ExtTextOut.
/////   ScriptPlace
//
//      The ScriptPlace function takes the output of a ScriptShape call and
//      generates glyph advance width and 2D offset information.
//
//      The composite ABC width for the whole item identifies how much the
//      glyphs overhang to the left of the start position and to the right of
//      the length implied by the sum of the advance widths.
//
//      The total advance width of the line is exactly abcA + abcB + abcC.
//
//      abcA and abcC are maintained internally by Uniscribe as proportions
//      of the cell height represented in 8 bits and are thus roughly +/- 1%.
//      The total width returned (as the sum of piAdvance, and as the sum of
//      abcA+abcB+abcC) is accurate to the resolution of the TrueType shaping
//      engine.
//
//      All glyph related arrays are in visual order unless the fLogicalOrder
//      flag is set in psa.

type
  PGOffset = ^TGOffset;
  tagGOFFSET = record
    du:  Integer;
    dv:  Integer;
  end;
  GOFFSET = tagGOFFSET;
  {$EXTERNALSYM GOFFSET}
  TGOffset = tagGOFFSET;


function ScriptPlace(
    hdc: HDC;                   // In    Optional (see under caching)
    psc: PScriptCache;          // InOut Cache handle
    const pwGlyphs: PWord;      // In    Glyph buffer from prior ScriptShape call
    cGlyphs: Integer;           // In    Number of glyphs
    const psva: PScriptVisAttr; // In    Visual glyph attributes
    psa: PScriptAnalysis;       // InOut Result of ScriptItemize (may have fNoGlyphIndex set)
    piAdvance: PInteger;        // Out   Advance wdiths
    pGoffset: PGOffset;         // Out   x,y offset for combining glyph
    pABC: PABC                  // Out   Composite ABC for the whole run (Optional)
 ): HRESULT; stdcall;
{$EXTERNALSYM ScriptPlace}






/////   ScriptTextOut
//
//      The ScriptTextOut function takes the output of both ScriptShape and
//      ScriptPlace calls and calls the operating system ExtTextOut function
//      appropriately. If the last parameter is not null, GDI's ExtTextOutW calls
//      are routed to this function.
//
//      All arrays are in visual order unless the fLogicalOrder flag is set in
//      psa.


function ScriptTextOut(
    const hdc: HDC;                // In     OS handle to device context (required)
    psc: PScriptCache;             // InOut  Cache handle
    x: Integer;                    // In     x,y position for first glyph
    y: Integer;                    // In
    fuOptions: LongWord;           // In     ExtTextOut options
    const lprc: PRect;             // In     optional clipping/opaquing rectangle
    const psa: PScriptAnalysis;    // In     Result of ScriptItemize
    const pwcReserved: PWideChar;  // In     Reserved (requires NULL)
    iReserved: Integer;            // In     Reserved (requires 0)
    const pwGlyphs: PWord;         // In     Glyph buffer from prior ScriptShape call
    cGlyphs: Integer;              // In     Number of glyphs
    const piAdvance: PInteger;     // In     Advance widths from ScriptPlace
    const piJustify: PInteger;     // In     Justified advance widths (optional)
    const pGoffset: PGOffset       // In     x,y offset for combining glyph
 ): HRESULT; stdcall;






/////
//
//      The caller should normally use SetTextAlign(hdc, TA_RIGHT) before
//      calling ScriptTextOut with an RTL item inlogical order.
//
//      The piJustify array provides requested cell widths for each glyph.
//      When the piJustify width of a glyph differs from the unjustified
//      width (in PiAdvance), space is added to or removed from the glyph
//      cell at it's trailing edge. The glyph is always aligned with the
//      leading edge of it's cell. (This rule applies even in visual order.)
//
//      When a glyph cell is extended the extra space is uaually made up by
//      the addition of white space, however for Arabic scripts, the extra
//      space is made up by one or more kashida glyphs, unless the extra space
//      is insufficient for the shortest kashida glyph in the font. (The
//      width of the shortest kashida is available by calling
//      ScriptGetFontProperties.)
//
//      piJustify should only be passed if re-justification of the string is
//      required. Normally pass NULL to this parameter.
//
//      fuOptions may contain ETO_CLIPPED or ETO_OPAQUE (or neither or both).
//
//      Do not use ScriptTextOut to write to a metafile unless you are sure
//      that the metafile will eventually be played back without any font
//      substitution. ScriptTextOut record glyph numbers in the metafile.
//      Since glyph numbers vary considerably from one font to another
//      such a metafile is unlikely to play back correctly when differant
//      fonts are substituted.
//
//      For example when a metafile is played back at a different scale
//      CreateFont requests recorded in the metafile may resolve to bitmap
//      instead of truetype fonts, or if the metafile is played back on
//      a different machine requested fonts may not be installed.//
//
//      To write complex scripts in a metafile in a font independant manner,
//      use ExtTextOut to write the logical characters directly, so that
//      glyph generation and placement does not occur until the text is
//      played back.
/////   ScriptJustify
//
//      ScriptJustify provides a simple minded implementation of multilingual
//      justification.
//
//      Sophisticated text formatters may prefer to generate their own delta
//      dx array by combining their own features with the information returned
//      by ScriptShape in the SCRIPT_VISATTR array.
//
//      ScriptJustify establishes how much adjustment to make at each glyph
//      position on the line. It interprets the SCRIPT_VISATTR array generated
//      by a call to ScriptShape, and gives top priority to kashida, then uses
//      inter word spacing if there's no kashida points, then uses
//      intercharacter spacing if there are no inter-word points.
//
//      The justified advance widths generated in ScriptJustify should be
//      passed to ScriptTextOut in the piJustify paramter.
//
//      ScriptJustify creates a justify array containing updated advance
//      widths for each glyph. Where a glyphs advance width is increased, it
//      is expected that the extra width will be rendered to the right of the
//      glyph, with as white space or, for Arabic text, as kashida.
/////
function ScriptJustify(
    const psva:  PScriptVisAttr;  // In   Collected visual attributes for entire line
    const piAdvance: PInteger;    // In   Advance widths from ScriptPlace
    cGlyphs: Integer;             // In   Size of all arrays
    iDx: Integer;                 // In   Desired width change, either increase or descrease
    iMinKashida: Integer;         // In   Minimum length of continuous kashida glyph to generate
    piJustify: PInteger           // Out  Updated advance widths to pass to ScriptTextOut
 ): HRESULT; stdcall;




type
  /////   SCRIPT_LOGATTR
  //
  //      The SCRIPT_LOGATTR structure describes attributes of logical
  //      characters useful when editing and formatting text.
  //
  //      Note that for wordbreaking and linebreaking, if the first character of
  //      the run passed in is not whitespace, the client needs to check whether
  //      the last character of the previous run is whitespace to determine if
  //      the first character of this run is the start of a word.
  //
  //
  TScriptLogAttr_enum = (
    fSoftBreak,  // Potential linebreak point
    fWhiteSpace, // A unicode whitespace character, except NBSP, ZWNBSP
    fCharStop,   // Valid cursor position (for left/right arrow)
    fWordStop,   // Valid cursor position (for ctrl + left/right arrow)
    fInvalid     // Invalid character sequence
  );

  PScriptLogAttr = ^TScriptLogAttr;
  tag_SCRIPT_LOGATTR = set of TScriptLogAttr_enum;
(*  fSoftBreak: Byte      {:1};     // Potential linebreak point
    fWhiteSpace: Byte     {:1};     // A unicode whitespace character, except NBSP, ZWNBSP
    fCharStop: Byte       {:1};     // Valid cursor position (for left/right arrow)
    fWordStop: Byte       {:1};     // Valid cursor position (for ctrl + left/right arrow)
    fInvalid: Byte        {:1};     // Invalid character sequence
    fReserved: Byte       {:3;}
  end; *)
  {$EXTERNALSYM tag_SCRIPT_LOGATTR}
  SCRIPT_LOGATTR = tag_SCRIPT_LOGATTR;
  {$EXTERNALSYM SCRIPT_LOGATTR}
  TScriptLogAttr = SCRIPT_LOGATTR;

  
//
//
//p     fSoftBreak: It would be valid to break the line in front of this
//              character. This flag is set on the first character of
//              South-East Asian words. Note that when linebreaking the
//              client would usually also treat any nonblank following a blank
//              as a softbreak position, by inspecting the fWhiteSPace flag
//              below.
//
//p     fWhiteSpace: This character is one of the many Unicode character
//              that are classified as breakable whitespace.
//
//p     fCharStop: Valid cursor position. Set on most characters, but not
//              on codepoints inside Indian and South East Asian character
//              clusters. May be used to implement left and right arrow
//              operation in editors.
//
//p     fWordStop: Valid position following word advance/retire commonly
//              implemented at ctrl/left-arrow and ctrl/right-arrow.
//              May be used to implement ctrl+left and ctrl+right arrow
//              operation in editors. As with fSoftBreak clients should
//              normally also inspect the fWhiteSpace flag and treat the
//              first character after a run of whitespace as the start of a
//              word.
//
//p     fInvalid: Marks characters which form an invalid or undisplayable
//              combination. Scripts which can set this flag have the flag
//              fInvalidLogAttr set in their SCRIPT_PROPERTIES.






/////   ScriptBreak
//
//      The ScriptBreak function returns cursor movement and formatting break
//      positions for an item as an array of SCRIPT_LOGATTRs. To support
//      mixed formatting within a single word correctly, ScriptBreak should
//      be passed whole items as returned by ScriptItemize.
//
//      ScriptBreak does not require an hdc and does not execute glyph shaping.
//
//      The fCharStop flag marks cluster boundaries for those scripts where
//      it is conventional to restrict from moving inside clusters. The same
//      boundaries could also be inferred by inspecting the pLogCLust array
//      returned by ScriptShape, however ScriptBreak is considerably faster in
//      implementation and does not require an hdc to be prepared.
//
//      The fWordStop, fSoftBreak and fWhiteSpace flags are only available
//      through ScriptBreak.
//
//      Most shaping engines that identify invalid sequences do so by setting
//      the fInvalid flag in ScriptBreak. The fInvalidLogAttr flag in
//      ScriptProperties identifies which scripts do this.


function ScriptBreak(
    const pwcChars: PWideChar;  // In   Logical unicode item
    cChars: Integer;    // In   Length of unicode item
    const psa: PScriptAnalysis;       // In   Result of earlier ScriptItemize call
    psla: PScriptLogAttr     // Out  Logical character attributes
 ): HRESULT; stdcall;
{$EXTERNALSYM ScriptBreak}





/////   ScriptCPtoX
//
//      The ScriptCPtoX function returns the x offset from the left end
//      (!fLogical) or leading edge (fLogical) of a run to either the leading
//      or the trailing edge of a logical character cluster.
//
//      iCP is the offset of any logical character in the cluster.
//
//      For scripts where the caret may conventionally be placed into the
//      middle of clusters (e.g. Arabic, Hebrew), the returned X may be
//      an interpolated position for any codepoint in the line.
//
//      For scripts where the caret is conventionally snapped to the boundaries
//      of clusters, (e.g. Thai, Indian), the resulting X position will be
//      snapped to the requested edge of the cluster containing CP.


function ScriptCPtoX(
    iCP: Integer;               // In   Logical character position in run
    fTrailing: BOOL;            // In   Which edge (default - leading)
    cChars: Integer;            // In   Count of logical codepoints in run
    cGlyphs: Integer;           // In   Count of glyphs in run
    const pwLogClust: PWord;    // In   Logical clusters
    const psva: PScriptVisAttr; // In   Visual glyph attributes array
    const piAdvance: PInteger;  // In   Advance widths
    const psa: PScriptAnalysis; // In   Script analysis from item attributes
    piX: PInteger               // Out  Resulting X position
 ): HRESULT; stdcall;






/////   ScriptXtoCP
//
//      The ScriptXtoCP function converts an x offset from the left end
//      (!fLogical) or leading edge (fLogical) of a run to a logical
//      character position and a flag that indicates whether the X position
//      fell in the leading or the trailing half of the character.
//
//      For scripts where the cursor may conventionally be placed into the
//      middle of clusters (e.g. Arabic, Hebrew), the returned CP may be
//      for any codepoint in the line, and fTrailing will be either zero
//      or one.
//
//      For scripts where the cursor is conventionally snapped to the
//      boundaries of a cluster, the returned CP is always the position of
//      the logically first codepoint in a cluster, and fTrailing is either
//      zero, or the number of codepoints in the cluster.
//
//      Thus the appropriate cursor position for a mouse hit is always the
//      returned CP plus the value of fTrailing.
//
//      If the X positition passed is not in the item at all, the resulting
//      position will be the trailing edge of character -1 (for X positions
//      before the item), or the leading edge of character 'cChars' (for
//      X positions following the item).


function ScriptXtoCP(
    iX: Integer;                 // In   X offset from left of run
    cChars: Integer;             // In   Count of logical codepoints in run
    cGlyphs: Integer;            // In   Count of glyphs in run
    const pwLogClust: PWord;     // In   Logical clusters
    const psva: PScriptVisAttr;  // In   Visual glyph attributes
    const piAdvance: Integer;    // In   Advance widths
    const psa:  PScriptAnalysis; // In   Script analysis from item attributes
    piCP: PInteger;              // Out  Resulting character position
    piTrailing: PInteger         // Out  Leading or trailing half flag
 ): HRESULT; stdcall;






/////   Relationship between caret positions, justifications points and clusters
//
//
//t     Job                              | Uniscribe support
//t     -------------------------------- | --------------------------------------------------------
//t     Caret move by character cluster  | LogClust or VISATTR.fClusterStart or LOGATTR.fCharStop
//t     Line breaking between characters | LogClust or VISATTR.fClusterStart or LOGATTR.fCharStop
//t     Caret move by word               | LOGATTR.fWordStop
//t     Line breaking between words      | LOGATTR.fWordStop
//t     Justification                    | VISATTR.uJustification
//
//
//
/////   Character clusters
//
//      Character clusters are glyph sequences that cannot be split between
//      lines.
//
//      Some languages (e.g. Thai, Indic) restrict caret placement to points
//      betwen clusters. This applies both to keyboard initiated caret
//      movement (e.g. cursor keys) and pointing and clicking with the mouse
//      (hit testing).
//
//      Uniscribe provides cluster information in both the visual and logical
//      attributes. If you've called ScriptShape you'll find the cluster
//      information represented both by sequences of the same value in the
//      pwLogClust array, and by the fClusterStart flag in the psva
//      SCRIPT_VISATTR array.
//
//      ScriptBreak also returns the fCharStop flag in the SCRIPT_LOGATTR
//      array to identify cluster positions.
//
//
//
/////   Word break points
//
//      Valid positions for moving the caret when moving in whole words are
//      marked by the fWordStop flag returned by ScriptBreak.
//
//      Valid positions for breaking lines between words are marked by the
//      fSoftBreak flag returned by ScriptBreak.
//
//
//
/////   Justification
//
//      Justification space or kashida should be inserted where identified by
//      the uJustificaion field of the SCRIPT_VISATTR.
//
//      When performing inter-character justification, insert extra space
//      only after glyphs marked with uJustify == SCRIPT_JUSTIFY_CHARACTER.
//
//
//
/////   Script specific processing
//
//      Uniscribe provides information about special processing for each
//      script in the SCRIPT_PROPERTIES array.
//
//      Use the following code during initialisation to get a pointer to
//      the SCRIPT_PROPERTIES array:
//
//c     const SCRIPT_PROPERTIES **g_ppScriptProperties; // Array of pointers to properties
//c     int iMaxScript;
//c     HRESULT hr;
//
//c     hr = ScriptGetProperties(&g_ppScriptProperties, &g_iMaxScript);
//
//      Then inspect the properties of the script of an item 'iItem' as follows:
//
//c     hr = ScriptItemize( ... , pItems, ... );
//c     ...
//c     if (g_ppScriptProperties[pItems[iItem].a.eScript]->fNeedsCaretInfo) {
//c         // Use ScriptBreak to restrict the caret from entering clusters (for example).
//c     }
//
//
//      SCRIPT_PROPERTIES.fNeedsCaretInfo
//
//      Caret placement should be restricted to cluster
//      edges for scripts such as Thai and Indian. The fNeedsCaretInfo flag
//      in SCRIPT_PROPERTIES identifies such languages.
//
//      Note that ScriptXtoCP and ScriptCPtoX automatically apply caret
//      placement restictions.
//
//
//      SCRIPT_PROPERTIES.fNeedsWordBreaking
//
//      For most scripts, word break placement  may be
//      identified by scanning for characters marked as fWhiteSpace in
//      SCRIPT_LOGATTR, or for glyphs marked as uJustify ==
//      SCRIPT_JUSTIFY_BLANK or SCRIPT_JUSTIFY_ARABIC_BLANK in SCRIPT_VISATTR.
//
//      For languages such as Thai, it is also necessary to call ScriptBreak,
//      and include character positions marked as fWordStop in SCRIPT_LOGATTR.
//      Such scripts are marked as fNeedsWordbreaking in SCRIPT_PROPERTIES.
//
//
//      SCRIPT_PROPERTIES.fNeedsCharacterJustify
//
//      Languages such as Thai also require inter-character spacing when
//      justifying (where uJustify == SCRIPT_JUSTIFY_CHARACTER in the
//      SCRIPT_VISATTR). Such languages are marked as fNeedsCharacterJustify
//      in SCRIPT_PROPERTIES.
//
//
//      SCRIPT_PROPERTIES.fAmbiguousCharSet
//
//      Many Uniscribe scripts do not correspond directly to 8 bit character
//      sets. For example Unicode characters in the range U+100 through U+024F
//      represent extended latin shapes used for many languages, including
//      those supported by EASTEUROPE_CHARSET, TURKISH_CHARSET and
//      VIETNAMESE_CHARSET. However many of these characters are supported by
//      more han one of thsese charsets.
//      fAmbiguousCharset is set for any script token which could contain
//      characters from a number of these charsets. In these cases the bCharSet
//      field may contain ANSI_CHARSET or DEFAULT_CHARSET. The Uniscribe client
//      will generally need to apply futher processing to determine which charset
//      to use when requesting a font suitable for this run. For example it
//      determine that the run consists of multiple languages and split it up
//      to use a different font for each language.






/////   Notes on ScriptXtoCP and ScriptCPtoX
//
//      Both functions work only within runs and require the results of a
//      previous ScriptShape call.
//
//      The client must establish which run a given cursor offset or x
//      position is within before passing it to ScriptCPtoX or ScriptXtoCP.
//
//      Cluster information in the logical cluster array is used to share
//      the width of a cluster of glyphs equally among the logical characters
//      they represent.
//
//      For example, the lam alif glyph is divided into four areas: the
//      leading half of the lam, the trailing half of the lam, the leading
//      half of the alif and the trailing half of the alif.
//
//      ScriptXtoCP Understands the caret position conventions of each script.
//      For Indian and Thai, caret positions are snapped to cluster boundaries,
//      for Arabic and Hebrew, caret positions are interpolated within clusters.
//
//
/////   Translating mouse hit 'x' offset to caret position
//
//      Conventionally, caret position 'cp' may be selected by clicking either
//      on the trailing half of character 'cp-1' or on the leading half of
//      character 'cp'. This may easily be implemented as follows:
//
//c     int iCharPos;
//c     int iCaretPos
//c     int fTrailing;
//
//c     ScriptXtoCP(iMouseX, ..., &iCharPos, &fTrailing);
//c     iCaretPos = iCharPos + fTrailing;
//
//      For scripts that snap the caret to cluster boundaries, ScriptXtoCP
//      returns ftrailing set to either 0, or the width of the cluster in
//      codepoints. Thus the above code correctly returns only valid
//      caret positions.
//
//
/////   Displaying the caret in bidi strings
//
//      In unidirectional text, the leading edge of a character is at the same
//      place as the trailing edge of the previous character, so there is no
//      ambiguity in placing the caret between characters.
//
//      In bidirectional text, the caret position between runs of opposing
//      direction may be ambiguous.
//
//      For example in the left to right paragraph 'helloMAALAS', the last
//      letter of 'hello' immediately preceeds the first letter of 'salaam'.
//      The best position to display the caret depends on whether it is
//      considered to follow the 'o' of 'hello', or to preceed the 's' of
//      'salaam'.
//
/////   Commonly used caret positioning conventions
//
//t     Situation       | Visual caret placement
//t     ---------       | -------------------------------------------
//t     Typing          | Trailing edge of last character typed
//t     Pasting         | Trailing edge of last character pasted
//t     Caret advancing | Trailing edge of last character passed over
//t     Caret retiring  | Leading edge of last character passed over
//t     Home            | Leading edge of line
//t     End             | Trailing edge of line
//
//      The caret may be positioned as follows:
//
//c     if (advancing) {
//c         ScriptCPtoX(iCharPos-1, TRUE, ..., &iCaretX);
//c     } else {
//c         ScriptCPtoX(iCharPos, FALSE, ..., &iCaretX);
//c     }
//
//      Or, more simply, given an fAdvancing BOOL restricted to TRUE or FALSE:
//
//c     ScriptCPtoX(iCharPos-fAdvancing, fAdvancing, ..., &iCaretX);
//
//      ScriptCPtoX handles out of range positions logically: it returns the
//      leading edge of the run for iCharPos <0, and the trailing edge of the
//      run for iCharPos >=length.
/////   ScriptGetLogicalWidths
//
//      Converts visual withs in piAdvance into logical widths,
//      one per original character, in logical order.
//
//      Ligature glyphs widths are divided evenly amongst the characters
//      they represent.


function ScriptGetLogicalWidths(
    const psa: PScriptAnalysis;     // In   Script analysis from item attributes
    cChars: Integer;                // In   Count of logical codepoints in run
    cGlyphs: Integer;               // In   Count of glyphs in run
    const piGlyphWidth: PInteger;   // In   Advance widths
    const pwLogClust: PWord;        // In   Logical clusters
    const psva: PScriptVisAttr;     // In   Visual glyph attributes
    piDx: PInteger                  // Out  Logical widths
 ): HRESULT; stdcall;






/////
//      ScriptGetLogicalWidths is useful for recording widths in a
//      font independant manner. By passing the recorded logical widths
//      to ScriptApplyLogicalWidths, a block of text can be replayed in the
//      same boundaries with acceptable loss of quality even when the original
//      font is not available.
/////   ScriptApplyLogicalWidth
//
//      Accepts an array of advance widths in logical order, corresponding
//      one to one with codepoints, and generates an array of glyph widths
//      suitable for passing to the piJustify parameter of ScriptTextOut.
//
//      ScriptApplyLogicalWidth may be used to reapply logical widths
//      obtained with ScriptGetLogicalWidths. It may be useful in situations
//      such as metafiling, where it is necessary to record and reapply
//      advance width information in a font independant manner.



function ScriptApplyLogicalWidth(
    const piDx: PInteger;        // In     Logical dx array to apply
    cChars: Integer;             // In     Count of logical codepoints in run
    cGlyphs: Integer;            // In     Glyph count
    const pwLogClust: PWORD;     // In     Logical clusters
    const psva: PScriptVisAttr;  // In     Visual attributes from ScriptShape/Place
    const piAdvance: PInteger;   // In     Glyph advance widths from ScriptPlace
    const psa:  PScriptAnalysis; // In     Script analysis from item attributes
    pABC: PABC;                  // InOut  Updated item ABC width (optional)
    piJustify: PInteger          // Out    Resulting glyph advance widths for ScriptTextOut
 ): HRESULT; stdcall;






/////
//p     piDx: Pointer to an array of dx widths in logical order, one per codepoint.
//
//p     cChars: Count of the logical codepoints in the run.
//
//p     cGlyphs: Glyph count.
//
//p     pwLogClust: Pointer to an array of logical clusters from ScriptShape
//
//p     psva: Pointer to an array of visual attributes from ScriptShape and
//          updated by ScriptPlace.
//
//p     piAdvance: Pointer to an array of glyph advance widths from ScriptPlace.
//
//p     psa: Pointer to a SCRIPT_ANALYSIS structure from ScriptItemize and
//          updated by ScriptShape and SriptPlace..
//
//p     pABC: Pointer to the run overall ABC width (optional). If present,
//          when the function is called, it should contain the run ABC width
//          returned by ScriptPlace; when the function returns, the ABC width
//          has been updated to match the new widths.
//
//p     piJustify:Pointer to an array of the resulting glyph advance widths.
//          This is suitable for passing to the piJustify parameter of ScriptTextOut.
/////   ScriptGetCMap
//
//      ScriptGetCMap may be used to determine which characters in a run
//      are supported by the selected font.
//
//      It returns glyph indices of Unicode characters according to Truetype
//      Cmap table, or standard Cmap implemented for old style fonts. The
//      glyph indices are returned in the same order as the input string.
//
//      The caller may scan the returned glyph buffer looking for the default
//      glyph to determine which characters are not available. (The default
//      glyph index for the selected font should be determined by calling
//      ScriptGetFontProperties).
//
//      The return value indicates the presence of any missing glyphs.

const
  SGCM_RTL  = $00000001;      // Return mirrored glyph for mirrorable Unicode codepoints


function ScriptGetCMap(
    hdc: HDC;                     // In    Optional (see notes on caching)
    psc: PScriptCache;            // InOut Address of Cache handle
    const pwcInChars: PWideChar;  // In    Unicode codepoint(s) to look up
    cChars: Integer;              // In    Number of characters
    dwFlags: DWORD;               // In    Flags such as SGCM_RTL
    pwOutGlyphs: PWord            // Out   Array of glyphs, one per input character
 ): HRESULT; stdcall;





/////
//  returns S_OK     - All unicode codepoints were present in the font
//          S_FALSE  - Some of the Unicode codepoints were mapped to the default glyph
//          E_HANDLE - font or system does not support glyph indices
/////   ScriptGetGlyphABCWidth
//
//      Returns ABC width of a given glyph.
//      May be useful for drawing glyph charts. Should not be used for
//      run of the mill complex script text formatting.


function ScriptGetGlyphABCWidth(
    hdc: HDC;            // In    Optional (see notes on caching)
    psc: PScriptCache;   // InOut Address of Cache handle
    wGlyph: Word;        // In    Glyph
    pABC: PABC           // Out   ABC width
 ): HRESULT; stdcall;





type
  /////
  //  returns S_OK     - Glyph width returned
  //          E_HANDLE - font or system does not support glyph indices
  /////   SCRIPT_PROPERTIES
  //

  TScriptProperties_enum = (
    fNumeric,              {:1}
    fComplex,              {:1}  // Script requires special shaping or layout
    fNeedsWordBreaking,    {:1}  // Requires ScriptBreak for word breaking information
    fNeedsCaretInfo,       {:1}  // Requires caret restriction to cluster boundaries
    bCharSet,              {:8}  // Charset to use when creating font
    fControl,              {:1}  // Contains only control characters
    fPrivateUseArea,       {:1}  // This item is from the Unicode range U+E000 through U+F8FF
    fNeedsCharacterJustify,{:1}  // Requires inter-character justification
    fInvalidGlyph,         {:1}  // Invalid combinations generate glyph wgInvalid in the glyph buffer
    fInvalidLogAttr,       {:1}  // Invalid combinations are marked by fInvalid in the logical attributes
    fCDM,                  {:1}  // Contains Combining Diacritical Marks
    fAmbiguousCharSet,     {:1}  // Script does not correspond 1//:1 with a charset
    fClusterSizeVaries,    {:1}  // Measured cluster width depends on adjacent clusters
    fRejectInvalid         {:1}  // Invalid combinations should be rejected
  );
  TScriptProperties_set = set of TScriptProperties_enum;

  PScriptProperties = ^TScriptProperties;
  SCRIPT_PROPERTIES = packed record
    langid: Word                  {:16}; // Primary and sublanguage associated with script
    fFlags: TScriptProperties_set;
  end;
(*  langid: DWORD                 {:16}; // Primary and sublanguage associated with script
    fNumeric: DWORD               {:1};
    fComplex: DWORD               {:1};  // Script requires special shaping or layout
    fNeedsWordBreaking: DWORD     {:1};  // Requires ScriptBreak for word breaking information
    fNeedsCaretInfo: DWORD        {:1};  // Requires caret restriction to cluster boundaries
    bCharSet: DWORD               {:8};  // Charset to use when creating font
    fControl: DWORD               {:1};  // Contains only control characters
    fPrivateUseArea: DWORD        {:1};  // This item is from the Unicode range U+E000 through U+F8FF
    fNeedsCharacterJustify: DWORD {:1};  // Requires inter-character justification
    fInvalidGlyph: DWORD          {:1};  // Invalid combinations generate glyph wgInvalid in the glyph buffer
    fInvalidLogAttr: DWORD        {:1};  // Invalid combinations are marked by fInvalid in the logical attributes
    fCDM: DWORD                   {:1};  // Contains Combining Diacritical Marks
    fAmbiguousCharSet: DWORD      {:1};  // Script does not correspond 1//:1 with a charset
    fClusterSizeVaries: DWORD     {:1};  // Measured cluster width depends on adjacent clusters
    fRejectInvalid: DWORD         {:1};  // Invalid combinations should be rejected
  end; *)
  {$EXTERNALSYM SCRIPT_PROPERTIES}
  TScriptProperties = SCRIPT_PROPERTIES;
  
//
//p     langid: Language associated with this script. When a script is used for many languages,
//          langid id represents a default language. For example, Western script is represented
//          by LANG_ENGLISH although it is also used for French, German, Spanish etc.
//
//p     fNumeric: Script contains numerics and characters used in conjunction with numerics
//          by the rules of the Unicode bidirectional algorithm. For example
//          dollar sign and period are classified as numeric when adjacent to or in between
//          digits.
//
//p     fComplex: Indicates a script that requires complex script handling. If fComplex is false
//          the script contains no combining characters and requires no contextual shaping or reordering.
//
//p     fNeedsWordBreaking: A script, such as Thai, which requires algorithmic wordbreaking.
//          Use ScriptBreak to obtain a wordbreak points using the standard system wordbreaker.
//
//p     fNeedsCaretInfo: A script, such as Thai and Indian, where the caret may not be placed
//          inside a cluster. To determine valid caret positions inspect the fCharStop flag in the
//          logical attributes returned by ScriptBreak, or compare adjacent values in the pwLogClust
//          array returned by ScriptShape.
//
//p     bCharSet: Nominal charset associated with script. May be used in a logfont when creating
//          a font suitable for displaying this script. Note that for new scripts where there
//          is no charset defined, bCharSet may be innapropriate and DEFAULT_CHARSET should
//          be used instead - see the description of fAmbiguousCharSet below.
//
//p     fControl: contains control characters.
//
//p     fPrivateUseArea: The Unicode range U+E000 through U+F8FF.
//
//p     fNeedsCharacterJustify: A script, such as Thai, where justification is conventionally
//          achieved by increasing the space between all letters, not just between words.
//
//p     fInvalidGlyph: A script for which ScriptShape generates an invalid glyph
//          to represent invalid sequences. The glyph index of the invalid glyph for
//          a particular font may be obtained by calling ScriptGetFontProperties.
//
//p     fInvalidLogAttr: A script for which ScriptBreak sets the fInvalid flag
//          in the logical attributes to mark invalid sequences.
//
//p     fCDM: Implies that an item analysed by ScriptItemize included combining
//          diacritical marks (U+0300 through U+36F).
//
//p     fAmbiguousCharSet: No single legacy charset supports this script.
//          For example the extended Latin Extended-A Unicode range includes
//          characters from the EASTUROPE_CHARSET, the TURKISH_CHARSET and the
//          BALTIC_CHARSET. It also contains characters that are not available
//          in any legacy charset. Use DEFAULT_CHARSET when creating fonts to
//          display parts of this run.
//
//p     fClusterSizeVaries: A script, such as Arabic, where contextual shaping
//          may cause a string to increase in size when removing characters.
//
//p     fRejectInvalid: A script, such as Thai, where invalid sequences conventionally
//          cause an editor such as notepad to beep, and ignore keypresses.


/////   ScriptGetProperties
//
//      ScriptGetProperties returns the address of a table that maps a
//      script in a SCRIPT_ANALYSIS uScript field to properties including
//      the primary language associated with that script, whether it's
//      numeric and whether it's complex.


function ScriptGetProperties(
    out ppSp: PScriptProperties;   // Out  Receives pointer to table of pointers to properties indexed by script
    out piNumScripts: Integer      // Out  Receives number of scripts (valid values are 0 through NumScripts-1)
 ): HRESULT; stdcall;





type
  /////   SCRIPT_FONTPROPERTIES
  //
  PScriptFontProperties = ^TScriptFontProperties;
  SCRIPT_FONTPROPERTIES = record
    cBytes: Integer;      // Structure length
    wgBlank: Word;        // Blank glyph
    wgDefault: Word;      // Glyph used for Unicode values not present in the font
    wgInvalid: Word;      // Glyph used for invalid character combinations (especially in Thai)
    wgKashida: Word;      // Shortest continuous kashida glyph in the font, -1 if doesn't exist
    iKashidaWidth: Integer;// Widths of shortest continuous kashida glyph in the font
  end;
  {$EXTERNALSYM SCRIPT_FONTPROPERTIES}
  TScriptFontProperties = SCRIPT_FONTPROPERTIES;


/////   ScriptGetFontProperties
//
//      Returns information from the font cache


function ScriptGetFontProperties(
    hdc: HDC;                    // In    Optional (see notes on caching)
    psc: PScriptCache;           // InOut Address of Cache handle
    sfp:  PScriptFontProperties  // Out   Receives properties for this font
 ): HRESULT; stdcall;






/////   ScriptCacheGetHeight
//
//


function ScriptCacheGetHeight(
    hdc: HDC;            // In    Optional (see notes on caching)
    psc: PScriptCache;   // InOut Address of Cache handle
    tmHeight: PInteger   // Out   Receives font height in pixels
 ): HRESULT; stdcall;




const
  /////   ScriptStringAnalyse
  //
  //
  SSA_PASSWORD         = $00000001;  // Input string contains a single character to be duplicated iLength times
  SSA_TAB              = $00000002;  // Expand tabs
  SSA_CLIP             = $00000004;  // Clip string at iReqWidth
  SSA_FIT              = $00000008;  // Justify string to iReqWidth
  SSA_DZWG             = $00000010;  // Provide representation glyphs for control characters
  SSA_FALLBACK         = $00000020;  // Use fallback fonts
  SSA_BREAK            = $00000040;  // Return break flags (character and word stops)
  SSA_GLYPHS           = $00000080;  // Generate glyphs, positions and attributes
  SSA_RTL              = $00000100;  // Base embedding level 1
  SSA_GCP              = $00000200;  // Return missing glyphs and LogCLust with GetCharacterPlacement conventions
  SSA_HOTKEY           = $00000400;  // Replace '&' with underline on subsequent codepoint
  SSA_METAFILE         = $00000800;  // Write items with ExtTextOutW Unicode calls, not glyphs
  SSA_LINK             = $00001000;  // Apply FE font linking/association to non-complex text
  SSA_HIDEHOTKEY       = $00002000;  // Remove first '&' from displayed string
  SSA_HOTKEYONLY       = $00002400;  // Display underline only.

  SSA_FULLMEASURE      = $04000000;  // Internal - calculate full width and out the number of chars can fit in iReqWidth.
  SSA_LPKANSIFALLBACK  = $08000000;  // Internal - enable FallBack for all LPK Ansi calls Except BiDi hDC calls
  SSA_PIDX             = $10000000;  // Internal
  SSA_LAYOUTRTL        = $20000000;  // Internal - Used when DC is mirrored
  SSA_DONTGLYPH        = $40000000;  // Internal - Used only by GDI during metafiling - Use ExtTextOutA for positioning
  SSA_NOKASHIDA        = $80000000;  // Internal - Used by GCP to justify the non Arabic glyphs only.
//
//
//p     SSA_HOTKEY: Note that SSA_HOTKEY and SSA_HIDEHOTKEY remove the
//          hotkey '&' character from further processing, so functions
//          such as ScriptString_pLogAttr return arrays based on a string
//          which excludes the '&'.



type
  /////   SCRIPT_TABDEF
  //
  //      Defines tabstop positions for ScriptStringAnalyse (ignored unless SSA_TAB passed)
  //
  PScriptTabDef = ^TScriptTabDef;
  tag_SCRIPT_TABDEF = record
    cTabStops: Integer;        // Number of entries in pTabStops array
    iScale: Integer;           // Scale factor for pTabStops (see below)
    pTabStops: PInteger;       // Pointer to array of one or more tab stops
    iTabOrigin: Integer;       // Initial offset for tab stops (logical units)
  end;
  {$EXTERNALSYM tag_SCRIPT_TABDEF}
  SCRIPT_TABDEF = tag_SCRIPT_TABDEF;
  {$EXTERNALSYM SCRIPT_TABDEF}
  TScriptTabDef = tag_SCRIPT_TABDEF;

//
//
//p     cTabStops: Number of entries in the pTabStops array. If zero, tabstops
//          are every 8 average character widths. If one, all tabstops are
//          the length of the first entry in pTabStops. If more than one,
//          the first cTabStops are as specified in the pTabStops array,
//          subsequent tabstops are every 8 average characters from the last
//          tabstop in the array.
//
//p     iScale: Scale factor for iTabOrigin and pTabStops entries. Values are
//          converted to device coordinates by multiplying by iScale then
//          dividing by 4. If values are already in device units, set iScale to
//          4. If values are in dialog units, set iScale to the average char
//          width of the dialog font. If values are multiples of the average
//          character width for the selected font, set iScale to 0.
//
//p     pTabStops: Array of cTabStops entries. Each entry specifies a
//          tabstop position. Positive values give nearedge alignment,
//          negative values give faredge alignment.
//
//p     iTabOrigin: Tabs are considered to start iTabOrigin before the
//          beginning of the string. Helps with multiple tabbed
//          outputs on the same line.






/////   ScriptStringAnalyse
//
//      cString - Input string must contain at least one character
//
//      hdc - required if SSA_GLYPH requested. Optional for SSA_BREAK.
//      If present the current font in the hdc is inspected and if a symbolic
//      font the character string is treated as a single neutral SCRIPT_UNDEFINED item.
//
//      Note that the uBidiLevel field in the initial SCRIPT_STATE value
//      is ignored - the uBidiLevel used is derived from the SSA_RTL
//      flag in combination with the layout of the hdc.


  SCRIPT_STRING_ANALYSIS = Pointer;
  {$EXTERNALSYM SCRIPT_STRING_ANALYSIS}
  TScriptStringAnalysis = SCRIPT_STRING_ANALYSIS;
  PScriptStringAnalysis = ^TScriptStringAnalysis;


function ScriptStringAnalyse(
    hdc: HDC;                  //In  Device context (required)
    const pString: Pointer;    //In  String in 8 or 16 bit characters
    cString: Integer;          //In  Length in characters (Must be at least 1)
    cGlyphs: Integer;          //In  Required glyph buffer size (default cString*1.5 + 16)
    iCharset: Integer;         //In  Charset if an ANSI string, -1 for a Unicode string
    dwFlags: DWORD;            //In  Analysis required
    iReqWidth: Integer;        //In  Required width for fit and/or clip
    psControl: PScriptControl; //In  Analysis control (optional)
    psState: PScriptState;     //In  Analysis initial state (optional)
    const piDx: PInteger;      //In  Requested logical dx array
    pTabdef: PScriptTabDef;    //In  Tab positions (optional)
    const pbInClass: PByte;    //In  Legacy GetCharacterPlacement character classifications (deprecated)

    pssa:  PScriptStringAnalysis //Out Analysis of string
 ): HRESULT; stdcall;






/////   ScriptStringFree - free a string analysis
//
//


function ScriptStringFree(
    pssa: PScriptStringAnalysis  //InOut Address of pointer to analysis
 ): HRESULT; stdcall;






/////   ScriptStringSize
//
//      returns a pointer to the size (width and height) of an analysed string
//
//      Note that the SIZE pointer remains valid only until the
//      SCRIPT_STRING_ANALYSIS is passed to ScriptStringFree.


function ScriptString_pSize(
    ssa: TScriptStringAnalysis
 ): {const} PSize; stdcall;






/////   ScriptString_pcOutChars
//
//      returns pointer to length of string after clipping (requires SSA_CLIP set)
//
//      Note that the int pointer remains valid only until the
//      SCRIPT_STRING_ANALYSIS is passed to ScriptStringFree.


function ScriptString_pcOutChars(
    ssa: TScriptStringAnalysis
 ): {const} PInteger; stdcall;






/////   ScriptString_pLogAttr
//
//      returns pointer to logical attributes buffer in a SCRIPT_STRING_ANALYSIS
//
//      Note that the buffer pointer remains valid only until the
//      SCRIPT_STRING_ANALYSIS is passed to ScriptStringFree.
//
//      The logical attribute array contains *ScriptString_pcOutChars(ssa)
//      entries.


function ScriptString_pLogAttr(
    ssa: TScriptStringAnalysis
 ): {const} PScriptLogAttr; stdcall;






/////   ScriptStringGetOrder
//
//      Creates an array mapping original character position to glyph position.
//
//      Treats clusters as they were in legacy systems - Unless a cluster
//      contains more glyphs than codepoints, each glyph is referenced at
//      least once from the puOrder array.
//
//      Requires SSA_GLYPHS requested in original ScriptStringAnalyse call.
//
//      The puOrder parameter should address a buffer containing room for
//      at least *ScriptString_pcOutChars(ssa) ints.


function ScriptStringGetOrder(
    ssa: TScriptStringAnalysis;
    puOrder: PLongWord
 ): HRESULT; stdcall;






/////   ScriptStringCPtoX
//
//      Return x coordinate for leading or trailing edge of character icp.


function ScriptStringCPtoX(
    ssa: TScriptStringAnalysis;        //In  String analysis
    icp: Integer;                      //In  Caret character position
    fTrailing: BOOL;                   //In  Which edge of icp
    out pX: Integer                    //Out Corresponding x offset
 ): HRESULT; stdcall;





/////   ScriptStringXtoCP
//
//


function ScriptStringXtoCP(
    ssa: TScriptStringAnalysis;        // In
    iX: Integer;                       // In
    piCh: PInteger;                    // Out
    piTrailing: PInteger               // Out
 ): HRESULT; stdcall;





/////   ScriptStringGetLogicalWidths
//
//      Converts visual withs in psa->piAdvance into logical widths,
//      one per original character, in logical order.
//
//      Requires SSA_GLYPHS requested in original ScriptStringAnalyse call.
//
//      The piDx parameter should address a buffer containing room for
//      at least *ScriptString_pcOutChars(ssa) ints.


function ScriptStringGetLogicalWidths(
    ssa: TScriptStringAnalysis;
    out piDx: Integer): HRESULT; stdcall;






/////   ScriptStringValidate
//
//      Scans the string analysis for invalid glyphs.
//
//      Only glyphs generated by scripts that can generate invalid glyphs
//      are scanned.
//
//      returns S_OK    - no invalid glyphs are present
//              S_FALSE - one or more invalid glyphs are present


function ScriptStringValidate(
    ssa: TScriptStringAnalysis): HRESULT; stdcall;






/////   ScriptStringOut
//
//      Displays the string generated by a prior ScriptStringAnalyze call,
//      then optionally adds highlighting corresponding to a logical selection.
//
//      Requires SSA_GLYPHS requested in original ScriptStringAnalyse call.


function ScriptStringOut(
    ssa: TScriptStringAnalysis;         //In  Analysis with glyphs
    iX: Integer;                        //In
    iY: Integer;                        //In
    uOptions: LongWord;                 //In  ExtTextOut options
    const prc: PRect;                   //In  Clipping rectangle (iff ETO_CLIPPED)
    iMinSel: Integer;                   //In  Logical selection. Set iMinSel>=iMaxSel for no selection
    iMaxSel: Integer;                   //In
    fDisabled: BOOL                     //In  If disabled, only the background is highlighted.
 ): HRESULT; stdcall;





const
  /////
  //      uOptions may nclude only ETO_CLIPPED or ETO_OPAQUE.
  /////   ScriptIsComplex
  //
  //      Determines whether a Unicode string requires complex script processing
  //
  //      The dwFlags parameter may include the following requests
  //
  SIC_COMPLEX     = 1;   // Treat complex script letters as complex
  SIC_ASCIIDIGIT  = 2;   // Treat digits U+0030 through U+0039 as complex
  SIC_NEUTRAL     = 4;   // Treat neutrals as complex
  
//
//      SIC_COMPLEX: Should normally set. Causes complex script letters to
//      be treated as complex.
//
//      SIC_ASCIIDIGIT: Set this flag if the string would be displayed with
//      digit substitution enabled. If you are following the users NLS
//      settings using the ScriptRecordDigitSubstitution API, you can pass
//      scriptDigitSubstitute.DigitSubstitute != SCRIPT_DIGITSUBSTITUTE_NONE.
//
//      SIC_NEUTRAL: Set this flag if you may be displaying the string with
//      right-to-left reading order. When this flag is set, neutral characters
//      are considered as complex.
//
//
//      Returns S_OK     if string requires complex script processing,
//              S_FALSE  if string contains only characters laid out side by
//                       side from left to right.


function ScriptIsComplex(
    const pwcInChars: PWideChar;     //In  String to be tested
    cInChars: Integer;               //In  Length in characters
    dwFlags: DWORD                   //In  Flags (see above)
 ): HRESULT; stdcall;





type
  /////   ScriptRecordDigitSubstitution
  //
  //      Reads NLS native digit and digit substitution settings and records
  //      them in the SCRIPT_DIGITSUBSTITUTE structure.
  //
  //
  PScriptDigitSubstitute = ^TScriptDigitSubstitute;
  tag_SCRIPT_DIGITSUBSTITUTE = packed record
    NationalDigitLanguage: Word    {:16};   // Language for native substitution
    TraditionalDigitLanguage: Word {:16};   // Language for traditional substitution
    DigitSubstitute: Byte          {:8};    // Substitution type
    bReserved: Byte;
    wReserved: Word;
    dwReserved: DWORD;                       // Reserved
  end;
(*  NationalDigitLanguage: DWORD    {:16};   // Language for native substitution
    TraditionalDigitLanguage: DWORD {:16};   // Language for traditional substitution
    DigitSubstitute: DWORD          {:8};    // Substitution type
    dwReserved: DWORD;                       // Reserved
  end; *)
  {$EXTERNALSYM tag_SCRIPT_DIGITSUBSTITUTE}
  SCRIPT_DIGITSUBSTITUTE = tag_SCRIPT_DIGITSUBSTITUTE;
  {$EXTERNALSYM SCRIPT_DIGITSUBSTITUTE}
  TScriptDigitSubstitute = tag_SCRIPT_DIGITSUBSTITUTE;
  
//
//
//p     NationalDigitLanguage: Standard digits for the selected locale as
//          defined by the countries standard setting authority.
//
//p     TraditionalDigitLangauge: Digits originally used with the locales
//          script.
//
//p     DigitSubstitute: Selects between None, Context, National and
//          Traditional. See ScriptApplyDigitSubstitution below for
//          constant definitions.
//
//      Although most complex scripts have their own associated digits, many
//      countries using those scripts use western (so called
//      'Arabic') digits as their standard. NationalDigitLanguage reflects the
//      digits used as standard, and is set from
//      the NLS data for the locale.
//      On Windows 2000 the national digit langauge can be
//      adjusted to any digit script with the control panel/regional
//      options/numbers/Standard digits listbox.
//
//      The TraditionalDigitLanguage for a locale is derived directly from the
//      script used by that locale.


function ScriptRecordDigitSubstitution(
    Locale: LCID;                     // In   LOCALE_USER_DEFAULT or desired locale
    out psds: TScriptDigitSubstitute  // Out  Digit substitution settings
 ): HRESULT; stdcall;





/////
//p     Locale: NLS locale to be queried. Should usually be set to
//          LOCALE_USER_DEFAULT. Alternatively may be passed as a locale
//          combined with LOCALE_NOUSEROVERRIDE to obtain default settings
//          for a given locale. Note that context digit substitution is
//          supported only in ARABIC and FARSI locales. In other locales,
//          context digit is mapped to no substitution.
//
//p     psds: Pointer to SCRIPT_DIGITSUBSTITUTE. This structure may be passed
//          later to ScriptApplyDigitSubstitution.
//
//p     returns: E_INVALIDARG if Locale is invalid or not installed. E_POINTER
//          if psds is NULL. Otherwise S_OK.
//
//      For performance reasons, you should not call
//      ScriptRecordDigitSubstitution frequently. In particular it would be a
//      considerable overhead to call it every time you call ScriptItemize
//      or ScriptStringAnalyse.
//
//      Instead, you may choose to save the SCRIPT_DIGITSUBSTITUTE
//      structure, and update it only when you receive a
//      WM_SETTINGCHANGE message or when a RegNotifyChangeKeyValue
//      call in a dedicated thread indicates a change in the registry
//      under HKCU\Control Panel\\International.
//
//      The normal way to call this function is simply
//
//c     SCRIPT_DIGITSUBSTITUTE sds;
//c     ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &sds);
//
//      Then every time you itemize, you'd use the results like this:
//
//c     SCRIPT_CONTROL  sc = {0};
//c     SCRIPT_STATE    ss = {0};
//
//c     ScriptApplyDigitSubstitution(&sds, &sc, &ss);
//
//
/////   ScriptApplyDigitSubstitution
//
//      Aplies the digit substitution settings recorded in a
//      SCRIPT_DIGIT_SUBSTITUTE structure to the SCRIPT_CONTROL and
//      SCRIPT_STATE structures.
//
//      The DigitSubstitute field of the SCRIPT_DIGITSUBSTITUTE structure
//      is normally set by ScriptRecordDigitSubstitution, however it may
//      be replaced by any one of the following values:
//
//
const
  SCRIPT_DIGITSUBSTITUTE_CONTEXT      = 0;  // Substitute to match preceeding letters
  SCRIPT_DIGITSUBSTITUTE_NONE         = 1;  // No substitution
  SCRIPT_DIGITSUBSTITUTE_NATIONAL     = 2;  // Substitute with official national digits
  SCRIPT_DIGITSUBSTITUTE_TRADITIONAL  = 3;  // Substitute with traditional digits of the locale
//
//
//p     SCRIPT_DIGITSUBSTITUTE_CONTEXT: Digits U+0030 - U+0039 will be
//          substituted according to the language of prior letters. Before
//          any letters, digits will be substituted according to the
//          TraditionalDigitLangauge field of the SCRIPT_DIGIT_SUBSTITUTE
//          structure. This field is normally set to the primary language of
//          the Locale passed to ScriptRecordDigitSubstitution.
//
//p     SCRIPT_DIGITSUBSTITUTE_NONE: Digits will not be substituted. Unicode
//          values U+0030 to U+0039 will be displayed with Arabic (i.e.
//          Western) numerals.
//
//p     SCRIPT_DIGITSUBSTITUTE_NATIONAL: Digits U+0030 - U+0039 will be
//          substituted according to the NationalDigitLangauge field of
//          the SCRIPT_DIGIT_SUBSTITUTE structure. This field is normally
//          set to the national digits returned for the NLS LCTYPE
//          LOCALE_SNATIVEDIGITS by ScriptRecordDigitSubstitution.
//
//p     SCRIPT_DIGITSUBSTITUTE_TRADITIONAL: Digits U+0030 - U+0039 will be
//          substituted according to the TraditionalDigitLangauge field of
//          the SCRIPT_DIGIT_SUBSTITUTE structure. This field is normally
//          set to the primary language of the Locale passed to
//          ScriptRecordDigitSubstitution.


function ScriptApplyDigitSubstitution(
    const psds: PScriptDigitSubstitute;   // In   Digit substitution settings
    psc: PScriptControl;                  // Out  Script control structure
    pss: PScriptState                     // Out  Script state structure
 ): HRESULT; stdcall;






/////
//p     psds: Pointer to SCRIPT_DIGITSUBSTITUTE structure recorded earlier.
//          If NULL, ScriptApplyDigitSubstitution calls
//          ScriptRecordDigitSubstitution with LOCALE_USER_DEFAULT.
//
//p     psc: SCRIPT_CONTROL structure. The fContextDigits and uDefaultLanguage
//          fields will be updated.
//
//p     pss: SCRIPT_CONTROL structure. The fDigitSubstitute field will be
//          updated.
//
//p     returns: E_INVALIDARG if the DigitSubstitute field of the
//          SCRIPT_DIGITSUBSTITUTE structure is unrecognised, else S_OK;

var
  Usp10IsInstalled: Boolean;

implementation

uses
  SysUtils;

const
  Usp10DLL = 'usp10.dll';

var
  Usp10DllModule: HMODULE = 0;

function GetUsp10DllModule: HMODULE;
begin
  if Usp10DllModule = 0 then
  begin
    Usp10DllModule := SafeLoadLibrary(Usp10DLL);
    if Usp10DllModule <= HINSTANCE_ERROR then
      Usp10DllModule := 0;
  end;
  Result := Usp10DllModule;
end;

procedure GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: string);
begin
  if not Assigned(P) then
  begin
    P := Pointer(GetProcAddress(Usp10DllModule, PAnsiChar(AnsiString(ProcName))));
    if not Assigned(P) or (Usp10DllModule = 0) then
      RaiseLastOSError;
  end;
end;

var
  _ScriptFreeCache: Pointer = nil;

function ScriptFreeCache;
begin
  if _ScriptFreeCache = nil then
    GetProcedureAddress(_ScriptFreeCache, Usp10DLL, 'ScriptFreeCache');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptFreeCache]
  end;
end;

var
  _ScriptItemize: Pointer = nil;

function ScriptItemize;
begin
  if _ScriptItemize = nil then
    GetProcedureAddress(_ScriptItemize, Usp10DLL, 'ScriptItemize');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptItemize]
  end;
end;

var
  _ScriptLayout: Pointer = nil;

function ScriptLayout;
begin
  if _ScriptLayout = nil then
    GetProcedureAddress(_ScriptLayout, Usp10DLL, 'ScriptLayout');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptLayout]
  end;
end;

var
  _ScriptShape: Pointer = nil;

function ScriptShape;
begin
  if _ScriptShape = nil then
    GetProcedureAddress(_ScriptShape, Usp10DLL, 'ScriptShape');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptShape]
  end;
end;

var
  _ScriptPlace: Pointer = nil;

function ScriptPlace;
begin
  if _ScriptPlace = nil then
    GetProcedureAddress(_ScriptPlace, Usp10DLL, 'ScriptPlace');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptPlace]
  end;
end;

var
  _ScriptTextOut: Pointer = nil;

function ScriptTextOut;
begin
  if _ScriptTextOut = nil then
    GetProcedureAddress(_ScriptTextOut, Usp10DLL, 'ScriptTextOut');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptTextOut]
  end;
end;

var
  _ScriptJustify: Pointer = nil;

function ScriptJustify;
begin
  if _ScriptJustify = nil then
    GetProcedureAddress(_ScriptJustify, Usp10DLL, 'ScriptJustify');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptJustify]
  end;
end;

var
  _ScriptBreak: Pointer = nil;

function ScriptBreak;
begin
  if _ScriptBreak = nil then
    GetProcedureAddress(_ScriptBreak, Usp10DLL, 'ScriptBreak');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptBreak]
  end;
end;

var
  _ScriptCPtoX: Pointer = nil;

function ScriptCPtoX;
begin
  if _ScriptCPtoX = nil then
    GetProcedureAddress(_ScriptCPtoX, Usp10DLL, 'ScriptCPtoX');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptCPtoX]
  end;
end;

var
  _ScriptXtoCP: Pointer = nil;

function ScriptXtoCP;
begin
  if _ScriptXtoCP = nil then
    GetProcedureAddress(_ScriptXtoCP, Usp10DLL, 'ScriptXtoCP');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptXtoCP]
  end;
end;

var
  _ScriptGetLogicalWidths: Pointer = nil;

function ScriptGetLogicalWidths;
begin
  if _ScriptGetLogicalWidths = nil then
    GetProcedureAddress(_ScriptGetLogicalWidths, Usp10DLL, 'ScriptGetLogicalWidths');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptGetLogicalWidths]
  end;
end;

var
  _ScriptApplyLogicalWidth: Pointer = nil;

function ScriptApplyLogicalWidth;
begin
  if _ScriptApplyLogicalWidth = nil then
    GetProcedureAddress(_ScriptApplyLogicalWidth, Usp10DLL, 'ScriptApplyLogicalWidth');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptApplyLogicalWidth]
  end;
end;

var
  _ScriptGetCMap: Pointer = nil;

function ScriptGetCMap;
begin
  if _ScriptGetCMap = nil then
    GetProcedureAddress(_ScriptGetCMap, Usp10DLL, 'ScriptGetCMap');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptGetCMap]
  end;
end;

var
  _ScriptGetGlyphABCWidth: Pointer = nil;

function ScriptGetGlyphABCWidth;
begin
  if _ScriptGetGlyphABCWidth = nil then
    GetProcedureAddress(_ScriptGetGlyphABCWidth, Usp10DLL, 'ScriptGetGlyphABCWidth');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptGetGlyphABCWidth]
  end;
end;

var
  _ScriptGetProperties: Pointer = nil;

function ScriptGetProperties;
begin
  if _ScriptGetProperties = nil then
    GetProcedureAddress(_ScriptGetProperties, Usp10DLL, 'ScriptGetProperties');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptGetProperties]
  end;
end;

var
  _ScriptGetFontProperties: Pointer = nil;

function ScriptGetFontProperties;
begin
  if _ScriptGetFontProperties = nil then
    GetProcedureAddress(_ScriptGetFontProperties, Usp10DLL, 'ScriptGetFontProperties');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptGetFontProperties]
  end;
end;

var
  _ScriptCacheGetHeight: Pointer = nil;

function ScriptCacheGetHeight;
begin
  if _ScriptCacheGetHeight = nil then
    GetProcedureAddress(_ScriptCacheGetHeight, Usp10DLL, 'ScriptCacheGetHeight');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptCacheGetHeight]
  end;
end;

var
  _ScriptStringAnalyse: Pointer = nil;

function ScriptStringAnalyse;
begin
  if _ScriptStringAnalyse = nil then
    GetProcedureAddress(_ScriptStringAnalyse, Usp10DLL, 'ScriptStringAnalyse');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringAnalyse]
  end;
end;

var
  _ScriptStringFree: Pointer = nil;

function ScriptStringFree;
begin
  if _ScriptStringFree = nil then
    GetProcedureAddress(_ScriptStringFree, Usp10DLL, 'ScriptStringFree');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringFree]
  end;
end;

var
  _ScriptString_pSize: Pointer = nil;

function ScriptString_pSize;
begin
  if _ScriptString_pSize = nil then
    GetProcedureAddress(_ScriptString_pSize, Usp10DLL, 'ScriptString_pSize');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptString_pSize]
  end;
end;

var
  _ScriptString_pcOutChars: Pointer = nil;

function ScriptString_pcOutChars;
begin
  if _ScriptString_pcOutChars = nil then
    GetProcedureAddress(_ScriptString_pcOutChars, Usp10DLL, 'ScriptString_pcOutChars');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptString_pcOutChars]
  end;
end;

var
  _ScriptString_pLogAttr: Pointer = nil;

function ScriptString_pLogAttr;
begin
  if _ScriptString_pLogAttr = nil then
    GetProcedureAddress(_ScriptString_pLogAttr, Usp10DLL, 'ScriptString_pLogAttr');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptString_pLogAttr]
  end;
end;

var
  _ScriptStringGetOrder: Pointer = nil;

function ScriptStringGetOrder;
begin
  if _ScriptStringGetOrder = nil then
    GetProcedureAddress(_ScriptStringGetOrder, Usp10DLL, 'ScriptStringGetOrder');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringGetOrder]
  end;
end;

var
  _ScriptStringCPtoX: Pointer = nil;

function ScriptStringCPtoX;
begin
  if _ScriptStringCPtoX = nil then
    GetProcedureAddress(_ScriptStringCPtoX, Usp10DLL, 'ScriptStringCPtoX');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringCPtoX]
  end;
end;

var
  _ScriptStringXtoCP: Pointer = nil;

function ScriptStringXtoCP;
begin
  if _ScriptStringXtoCP = nil then
    GetProcedureAddress(_ScriptStringXtoCP, Usp10DLL, 'ScriptStringXtoCP');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringXtoCP]
  end;
end;

var
  _ScriptStringGetLogicalWidths: Pointer = nil;

function ScriptStringGetLogicalWidths;
begin
  if _ScriptStringGetLogicalWidths = nil then
    GetProcedureAddress(_ScriptStringGetLogicalWidths, Usp10DLL, 'ScriptStringGetLogicalWidths');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringGetLogicalWidths]
  end;
end;

var
  _ScriptStringValidate: Pointer = nil;

function ScriptStringValidate;
begin
  if _ScriptStringValidate = nil then
    GetProcedureAddress(_ScriptStringValidate, Usp10DLL, 'ScriptStringValidate');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringValidate]
  end;
end;

var
  _ScriptStringOut: Pointer = nil;

function ScriptStringOut;
begin
  if _ScriptStringOut = nil then
    GetProcedureAddress(_ScriptStringOut, Usp10DLL, 'ScriptStringOut');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptStringOut]
  end;
end;

var
  _ScriptIsComplex: Pointer = nil;

function ScriptIsComplex;
begin
  if _ScriptIsComplex = nil then
    GetProcedureAddress(_ScriptIsComplex, Usp10DLL, 'ScriptIsComplex');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptIsComplex]
  end;
end;

var
  _ScriptRecordDigitSubstitution: Pointer = nil;

function ScriptRecordDigitSubstitution;
begin
  if _ScriptRecordDigitSubstitution = nil then
    GetProcedureAddress(_ScriptRecordDigitSubstitution, Usp10DLL, 'ScriptRecordDigitSubstitution');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptRecordDigitSubstitution]
  end;
end;

var
  _ScriptApplyDigitSubstitution: Pointer = nil;

function ScriptApplyDigitSubstitution;
begin
  if _ScriptApplyDigitSubstitution = nil then
    GetProcedureAddress(_ScriptApplyDigitSubstitution, Usp10DLL, 'ScriptApplyDigitSubstitution');
  asm
    MOV ESP, EBP
    POP EBP
    JMP [_ScriptApplyDigitSubstitution]
  end;
end;

initialization
  Usp10DllModule := GetUsp10DllModule;
  Usp10IsInstalled := Usp10DllModule <> 0;

finalization
  if Usp10DllModule <> 0 then FreeLibrary(Usp10DllModule);

end.
