You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
709 lines
19 KiB
709 lines
19 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* stringFormat.hpp
|
|
*
|
|
* Abstract:
|
|
*
|
|
* String and text format definition
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 08/05/1999 dbrown
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#ifndef _STRINGFORMAT_HPP
|
|
#define _STRINGFORMAT_HPP
|
|
|
|
const REAL DefaultMargin = REAL(1.0/6.0);
|
|
const REAL DefaultTracking = REAL(1.03);
|
|
const REAL DefaultBottomMargin = REAL(1.0/8.0);
|
|
|
|
const INT DefaultFormatFlags = 0;
|
|
|
|
const StringTrimming DefaultTrimming = StringTrimmingCharacter;
|
|
|
|
// Private StringFormatFlags
|
|
|
|
const INT StringFormatFlagsPrivateNoGDI = 0x80000000;
|
|
const INT StringFormatFlagsPrivateAlwaysUseFullImager = 0x40000000;
|
|
const INT StringFormatFlagsPrivateUseNominalAdvance = 0x20000000;
|
|
const INT StringFormatFlagsPrivateFormatPersisted = 0x10000000;
|
|
|
|
class StringFormatRecordData : public ObjectData
|
|
{
|
|
public:
|
|
INT32 Flags;
|
|
LANGID Language;
|
|
GpStringAlignment StringAlign;
|
|
GpStringAlignment LineAlign;
|
|
GpStringDigitSubstitute DigitSubstitute;
|
|
LANGID DigitLanguage;
|
|
REAL FirstTabOffset;
|
|
INT32 HotkeyPrefix;
|
|
REAL LeadingMargin;
|
|
REAL TrailingMargin;
|
|
REAL Tracking;
|
|
StringTrimming Trimming;
|
|
INT32 CountTabStops;
|
|
INT32 RangeCount;
|
|
};
|
|
|
|
//
|
|
// Represent a string format object
|
|
//
|
|
|
|
class GpStringFormat : public GpObject
|
|
{
|
|
protected:
|
|
VOID SetValid(BOOL valid)
|
|
{
|
|
GpObject::SetValid(valid ? ObjectTagStringFormat : ObjectTagInvalid);
|
|
}
|
|
|
|
public:
|
|
|
|
GpStringFormat() :
|
|
Flags (DefaultFormatFlags),
|
|
Language (LANG_NEUTRAL),
|
|
StringAlign (StringAlignmentNear),
|
|
LineAlign (StringAlignmentNear),
|
|
DigitSubstitute (StringDigitSubstituteUser),
|
|
DigitLanguage (LANG_NEUTRAL),
|
|
FirstTabOffset (0.0),
|
|
TabStops (NULL),
|
|
CountTabStops (0),
|
|
HotkeyPrefix (HotkeyPrefixNone),
|
|
LeadingMargin (DefaultMargin),
|
|
TrailingMargin (DefaultMargin),
|
|
Tracking (DefaultTracking),
|
|
Trimming (StringTrimmingCharacter),
|
|
Ranges (NULL),
|
|
RangeCount (0),
|
|
Permanent (FALSE)
|
|
{
|
|
SetValid(TRUE); // default is valid
|
|
}
|
|
|
|
GpStringFormat(INT flags, LANGID language) :
|
|
Flags (flags),
|
|
Language (language),
|
|
StringAlign (StringAlignmentNear),
|
|
LineAlign (StringAlignmentNear),
|
|
DigitSubstitute (StringDigitSubstituteUser),
|
|
DigitLanguage (LANG_NEUTRAL),
|
|
FirstTabOffset (0.0),
|
|
TabStops (NULL),
|
|
CountTabStops (0),
|
|
HotkeyPrefix (HotkeyPrefixNone),
|
|
LeadingMargin (DefaultMargin),
|
|
TrailingMargin (DefaultMargin),
|
|
Tracking (DefaultTracking),
|
|
Trimming (StringTrimmingCharacter),
|
|
Ranges (NULL),
|
|
RangeCount (0),
|
|
Permanent (FALSE)
|
|
{
|
|
SetValid(TRUE); // default is valid
|
|
}
|
|
|
|
~GpStringFormat()
|
|
{
|
|
if (TabStops)
|
|
delete [] TabStops;
|
|
|
|
if (Ranges)
|
|
delete [] Ranges;
|
|
}
|
|
|
|
|
|
static GpStringFormat *GenericDefault();
|
|
static GpStringFormat *GenericTypographic();
|
|
|
|
static void DestroyStaticObjects()
|
|
{
|
|
// these objects are created as a static but constructed in the
|
|
// GenericDefault() and GenericTypographic(). we need to destruct
|
|
// it just in case the user called SetTapStop which allocate memory
|
|
// inside this object and by this way we prevent any memory leak.
|
|
|
|
if (GenericDefaultPointer != NULL)
|
|
{
|
|
GenericDefaultPointer->~GpStringFormat();
|
|
|
|
// Zero the memory - this is risky code, so we want the memory
|
|
// to match what it was when GDI+ started up.
|
|
|
|
memset(GenericDefaultPointer, 0, sizeof(GpStringFormat));
|
|
|
|
GenericDefaultPointer = NULL;
|
|
}
|
|
|
|
if (GenericTypographicPointer != NULL)
|
|
{
|
|
GenericTypographicPointer->~GpStringFormat();
|
|
|
|
// Zero the memory - this is risky code, so we want the memory
|
|
// to match what it was when GDI+ started up.
|
|
|
|
memset(GenericTypographicPointer, 0, sizeof(GpStringFormat));
|
|
|
|
GenericTypographicPointer = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
GpStringFormat *Clone() const;
|
|
|
|
|
|
GpStatus SetFormatFlags(INT flags)
|
|
{
|
|
if (Flags != flags)
|
|
{
|
|
Flags = flags;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
INT GetFormatFlags() const
|
|
{
|
|
return Flags;
|
|
}
|
|
|
|
GpStatus SetAlign(GpStringAlignment align)
|
|
{
|
|
if (StringAlign != align)
|
|
{
|
|
StringAlign = align;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetAlign(GpStringAlignment *align) const
|
|
{
|
|
*align = StringAlign;
|
|
return Ok;
|
|
}
|
|
|
|
GpStringAlignment GetPhysicalAlignment() const
|
|
{
|
|
if ( !(Flags & StringFormatFlagsDirectionRightToLeft)
|
|
|| StringAlign == StringAlignmentCenter
|
|
|| Flags & StringFormatFlagsDirectionVertical)
|
|
{
|
|
return StringAlign;
|
|
}
|
|
else if (StringAlign == StringAlignmentNear)
|
|
{
|
|
return StringAlignmentFar; // RTL near = right
|
|
}
|
|
else
|
|
{
|
|
return StringAlignmentNear; // RTL far = left
|
|
}
|
|
}
|
|
|
|
GpStringAlignment GetAlign() const
|
|
{
|
|
return StringAlign;
|
|
}
|
|
|
|
|
|
GpStatus SetLineAlign(GpStringAlignment align)
|
|
{
|
|
if (LineAlign != align)
|
|
{
|
|
LineAlign = align;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetLineAlign(GpStringAlignment *align) const
|
|
{
|
|
*align = LineAlign;
|
|
return Ok;
|
|
}
|
|
|
|
GpStringAlignment GetLineAlign() const
|
|
{
|
|
return LineAlign;
|
|
}
|
|
|
|
|
|
GpStatus SetHotkeyPrefix (INT hotkeyPrefix)
|
|
{
|
|
if (HotkeyPrefix != hotkeyPrefix)
|
|
{
|
|
HotkeyPrefix = hotkeyPrefix;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetHotkeyPrefix (INT *hotkeyPrefix) const
|
|
{
|
|
if (!hotkeyPrefix)
|
|
return InvalidParameter;
|
|
|
|
*hotkeyPrefix = HotkeyPrefix;
|
|
return Ok;
|
|
}
|
|
|
|
INT GetHotkeyPrefix () const
|
|
{
|
|
return HotkeyPrefix;
|
|
}
|
|
|
|
|
|
GpStatus SetTabStops (
|
|
REAL firstTabOffset,
|
|
INT countTabStops,
|
|
const REAL *tabStops
|
|
)
|
|
{
|
|
if (countTabStops > 0)
|
|
{
|
|
// We do not support negative tabulation (tab position
|
|
// advances in the opposite direction of reading order)
|
|
|
|
if (firstTabOffset < 0)
|
|
{
|
|
return NotImplemented;
|
|
}
|
|
|
|
for (INT i = 0; i < countTabStops; i++)
|
|
{
|
|
if (tabStops[i] < 0)
|
|
{
|
|
return NotImplemented;
|
|
}
|
|
}
|
|
|
|
REAL *newTabStops = new REAL [countTabStops];
|
|
|
|
if (!newTabStops)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
if (TabStops)
|
|
{
|
|
delete [] TabStops;
|
|
}
|
|
|
|
TabStops = newTabStops;
|
|
|
|
GpMemcpy (TabStops, tabStops, sizeof(REAL) * countTabStops);
|
|
CountTabStops = countTabStops;
|
|
FirstTabOffset = firstTabOffset;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetTabStopCount (
|
|
INT *countTabStops
|
|
) const
|
|
{
|
|
if (!countTabStops)
|
|
{
|
|
return InvalidParameter;
|
|
}
|
|
|
|
*countTabStops = CountTabStops;
|
|
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetTabStops (
|
|
REAL *firstTabOffset,
|
|
INT countTabStops,
|
|
REAL *tabStops
|
|
) const
|
|
{
|
|
if ( !firstTabOffset
|
|
|| !tabStops)
|
|
{
|
|
return InvalidParameter;
|
|
}
|
|
|
|
INT count;
|
|
|
|
if (countTabStops <= CountTabStops)
|
|
count = countTabStops;
|
|
else
|
|
count = CountTabStops;
|
|
|
|
GpMemcpy(tabStops, TabStops, sizeof(REAL) * count);
|
|
|
|
*firstTabOffset = FirstTabOffset;
|
|
|
|
return Ok;
|
|
}
|
|
|
|
INT GetTabStops (
|
|
REAL *firstTabOffset,
|
|
REAL **tabStops
|
|
) const
|
|
{
|
|
*firstTabOffset = FirstTabOffset;
|
|
*tabStops = TabStops;
|
|
|
|
return CountTabStops;
|
|
}
|
|
|
|
|
|
GpStatus SetMeasurableCharacterRanges(
|
|
INT rangeCount,
|
|
const CharacterRange *ranges
|
|
);
|
|
|
|
|
|
INT GetMeasurableCharacterRanges(
|
|
CharacterRange **ranges = NULL
|
|
) const
|
|
{
|
|
if (ranges)
|
|
{
|
|
*ranges = Ranges;
|
|
}
|
|
return RangeCount;
|
|
}
|
|
|
|
|
|
GpStatus SetDigitSubstitution(
|
|
LANGID language,
|
|
StringDigitSubstitute substitute = StringDigitSubstituteNational
|
|
)
|
|
{
|
|
if (DigitSubstitute != substitute || DigitLanguage != language)
|
|
{
|
|
DigitSubstitute = substitute;
|
|
DigitLanguage = language;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetDigitSubstitution(
|
|
LANGID *language,
|
|
StringDigitSubstitute *substitute
|
|
) const
|
|
{
|
|
if(substitute)
|
|
{
|
|
*substitute = DigitSubstitute;
|
|
}
|
|
if(language)
|
|
{
|
|
*language = DigitLanguage;
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus SetLeadingMargin(REAL margin)
|
|
{
|
|
if (LeadingMargin != margin)
|
|
{
|
|
LeadingMargin = margin;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
REAL GetLeadingMargin() const
|
|
{
|
|
return LeadingMargin;
|
|
}
|
|
|
|
|
|
GpStatus SetTrailingMargin(REAL margin)
|
|
{
|
|
if (TrailingMargin != margin)
|
|
{
|
|
TrailingMargin = margin;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
REAL GetTrailingMargin() const
|
|
{
|
|
return TrailingMargin;
|
|
}
|
|
|
|
|
|
GpStatus SetTracking(REAL tracking)
|
|
{
|
|
if (Tracking != tracking)
|
|
{
|
|
Tracking = tracking;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
REAL GetTracking() const
|
|
{
|
|
return Tracking;
|
|
}
|
|
|
|
|
|
|
|
GpStatus SetTrimming(StringTrimming trimming)
|
|
{
|
|
if (Trimming != trimming)
|
|
{
|
|
Trimming = trimming;
|
|
UpdateUid();
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus GetTrimming(StringTrimming *trimming) const
|
|
{
|
|
ASSERT(trimming != NULL);
|
|
if (trimming == NULL)
|
|
{
|
|
return InvalidParameter;
|
|
}
|
|
*trimming = Trimming;
|
|
return Ok;
|
|
}
|
|
|
|
virtual BOOL IsValid() const
|
|
{
|
|
// If the string format came from a different version of GDI+, its tag
|
|
// will not match, and it won't be considered valid.
|
|
return GpObject::IsValid(ObjectTagStringFormat);
|
|
}
|
|
|
|
virtual ObjectType GetObjectType() const { return ObjectTypeStringFormat; }
|
|
|
|
virtual GpStatus GetData(IStream * stream) const
|
|
{
|
|
ASSERT (stream);
|
|
|
|
StringFormatRecordData stringFormatData;
|
|
|
|
stringFormatData.Flags = Flags;
|
|
stringFormatData.Language = Language;
|
|
stringFormatData.StringAlign = StringAlign;
|
|
stringFormatData.LineAlign = LineAlign;
|
|
stringFormatData.DigitSubstitute = DigitSubstitute;
|
|
stringFormatData.DigitLanguage = DigitLanguage;
|
|
stringFormatData.FirstTabOffset = FirstTabOffset;
|
|
stringFormatData.LineAlign = LineAlign;
|
|
stringFormatData.CountTabStops = CountTabStops;
|
|
stringFormatData.HotkeyPrefix = HotkeyPrefix;
|
|
stringFormatData.LeadingMargin = LeadingMargin;
|
|
stringFormatData.TrailingMargin = TrailingMargin;
|
|
stringFormatData.Tracking = Tracking;
|
|
stringFormatData.Trimming = Trimming;
|
|
stringFormatData.CountTabStops = CountTabStops;
|
|
stringFormatData.RangeCount = RangeCount;
|
|
|
|
stream->Write(
|
|
&stringFormatData,
|
|
sizeof(stringFormatData),
|
|
NULL
|
|
);
|
|
|
|
stream->Write(
|
|
TabStops,
|
|
CountTabStops * sizeof(TabStops[0]),
|
|
NULL
|
|
);
|
|
|
|
stream->Write(
|
|
Ranges,
|
|
RangeCount * sizeof(Ranges[0]),
|
|
NULL
|
|
);
|
|
|
|
return Ok;
|
|
}
|
|
|
|
virtual UINT GetDataSize() const
|
|
{
|
|
UINT size = sizeof(StringFormatRecordData);
|
|
|
|
size += (CountTabStops * sizeof(TabStops[0]));
|
|
size += (RangeCount * sizeof(Ranges[0]));
|
|
|
|
return size;
|
|
}
|
|
|
|
virtual GpStatus SetData(const BYTE * dataBuffer, UINT size)
|
|
{
|
|
if ((dataBuffer == NULL) || (size < (sizeof(StringFormatRecordData) - sizeof(REAL))))
|
|
{
|
|
WARNING(("dataBuffer too small"));
|
|
return InvalidParameter;
|
|
}
|
|
|
|
const StringFormatRecordData *stringFormatData =
|
|
(const StringFormatRecordData *)dataBuffer;
|
|
|
|
if (!stringFormatData->MajorVersionMatches())
|
|
{
|
|
WARNING(("Version number mismatch"));
|
|
return InvalidParameter;
|
|
}
|
|
|
|
Flags = stringFormatData->Flags | StringFormatFlagsPrivateFormatPersisted;
|
|
Language = stringFormatData->Language;
|
|
StringAlign = stringFormatData->StringAlign;
|
|
LineAlign = stringFormatData->LineAlign;
|
|
DigitSubstitute = stringFormatData->DigitSubstitute;
|
|
DigitLanguage = stringFormatData->DigitLanguage;
|
|
FirstTabOffset = stringFormatData->FirstTabOffset;
|
|
LineAlign = stringFormatData->LineAlign;
|
|
CountTabStops = stringFormatData->CountTabStops;
|
|
HotkeyPrefix = stringFormatData->HotkeyPrefix;
|
|
LeadingMargin = stringFormatData->LeadingMargin;
|
|
TrailingMargin = stringFormatData->TrailingMargin;
|
|
Tracking = stringFormatData->Tracking;
|
|
Trimming = stringFormatData->Trimming;
|
|
CountTabStops = stringFormatData->CountTabStops;
|
|
RangeCount = stringFormatData->RangeCount;
|
|
|
|
|
|
if (size <
|
|
( sizeof(StringFormatRecordData)
|
|
+ sizeof(TabStops[0]) * CountTabStops
|
|
+ sizeof(Ranges[0]) * RangeCount))
|
|
{
|
|
return InvalidParameter;
|
|
}
|
|
|
|
|
|
// Propagate tab stops
|
|
|
|
if (TabStops)
|
|
{
|
|
delete [] TabStops;
|
|
}
|
|
|
|
TabStops = new REAL [CountTabStops];
|
|
if (TabStops == NULL)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
REAL *tabStops = (REAL *)(&stringFormatData[1]);
|
|
for (INT i = 0; i < CountTabStops; i++)
|
|
{
|
|
TabStops[i] = tabStops[i];
|
|
}
|
|
|
|
|
|
// Propagate ranges
|
|
|
|
if (Ranges)
|
|
{
|
|
delete [] Ranges;
|
|
}
|
|
|
|
Ranges = new CharacterRange [RangeCount];
|
|
if (Ranges == NULL)
|
|
{
|
|
if (TabStops)
|
|
{
|
|
delete [] TabStops;
|
|
}
|
|
return OutOfMemory;
|
|
}
|
|
|
|
CharacterRange *ranges = (CharacterRange *)(&tabStops[CountTabStops]);
|
|
for (INT i = 0; i < RangeCount; i++)
|
|
{
|
|
Ranges[i] = ranges[i];
|
|
}
|
|
|
|
UpdateUid();
|
|
|
|
return Ok;
|
|
}
|
|
|
|
BOOL IsPermanent() const
|
|
{
|
|
return Permanent;
|
|
}
|
|
|
|
// we override the new operator for this class just to use it for object
|
|
// placement. we didn't make the new placemenet global because it will
|
|
// conflict with office because they link with GdiPlus statically.
|
|
// we didn't override the delete operator because it is fine to use the
|
|
// global one in \engine\runtime\Mem.h
|
|
|
|
void* operator new(size_t size)
|
|
{
|
|
return GpMalloc(size);
|
|
}
|
|
|
|
void* operator new(size_t size, void* p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
// Digit Substitution
|
|
const ItemScript GetDigitScript() const
|
|
{
|
|
return GetDigitSubstitutionsScript(DigitSubstitute, DigitLanguage);
|
|
}
|
|
|
|
private:
|
|
|
|
INT Flags;
|
|
LANGID Language;
|
|
GpStringAlignment StringAlign;
|
|
GpStringAlignment LineAlign;
|
|
GpStringDigitSubstitute DigitSubstitute;
|
|
LANGID DigitLanguage;
|
|
REAL FirstTabOffset;
|
|
REAL *TabStops; // absolute tab stops in world unit
|
|
INT CountTabStops;
|
|
INT HotkeyPrefix;
|
|
REAL LeadingMargin; // relative to body font em size
|
|
REAL TrailingMargin; // relative to body font em size
|
|
REAL Tracking; // scale factor
|
|
GpStringTrimming Trimming;
|
|
CharacterRange *Ranges; // character ranges
|
|
INT RangeCount; // number of ranges
|
|
BOOL Permanent;
|
|
|
|
GpStringFormat(const GpStringFormat &format)
|
|
{
|
|
// This should never get called! Use Clone instead!
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
|
|
// The following static variables support the generic StringFormats
|
|
|
|
// Pointers (initialised at load time to null)
|
|
|
|
static GpStringFormat *GenericDefaultPointer;
|
|
static GpStringFormat *GenericTypographicPointer;
|
|
|
|
// Memory allocation for generic structures. Note that we must allocate
|
|
// load time memory as BYTE arrays to avoid creating a dependency on
|
|
// the CRT for class construction.
|
|
// The definitions (which specify the size) are in StringFormat.cpp.
|
|
|
|
static BYTE GenericDefaultStaticBuffer[];
|
|
static BYTE GenericTypographicStaticBuffer[];
|
|
};
|
|
|
|
#endif // !_STRINGFORMAT_HPP
|
|
|