Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
e30607fd
Commit
e30607fd
authored
Jul 05, 2016
by
Nikolay Sivov
Committed by
Alexandre Julliard
Jul 05, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dwrite: Implement HasVerticalGlyphVariants().
Signed-off-by:
Nikolay Sivov
<
nsivov@codeweavers.com
>
Signed-off-by:
Alexandre Julliard
<
julliard@winehq.org
>
parent
90bcff0e
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
346 additions
and
12 deletions
+346
-12
dwrite_private.h
dlls/dwrite/dwrite_private.h
+1
-0
font.c
dlls/dwrite/font.c
+26
-12
opentype.c
dlls/dwrite/opentype.c
+116
-0
font.c
dlls/dwrite/tests/font.c
+203
-0
No files found.
dlls/dwrite/dwrite_private.h
View file @
e30607fd
...
...
@@ -229,6 +229,7 @@ extern UINT32 opentype_get_cpal_palettecount(const void*) DECLSPEC_HIDDEN;
extern
UINT32
opentype_get_cpal_paletteentrycount
(
const
void
*
)
DECLSPEC_HIDDEN
;
extern
HRESULT
opentype_get_cpal_entries
(
const
void
*
,
UINT32
,
UINT32
,
UINT32
,
DWRITE_COLOR_F
*
)
DECLSPEC_HIDDEN
;
extern
HRESULT
opentype_get_font_signature
(
struct
file_stream_desc
*
,
FONTSIGNATURE
*
)
DECLSPEC_HIDDEN
;
extern
BOOL
opentype_has_vertical_variants
(
IDWriteFontFace3
*
)
DECLSPEC_HIDDEN
;
struct
dwrite_colorglyph
{
USHORT
layer
;
/* [0, num_layers) index indicating current layer */
...
...
dlls/dwrite/font.c
View file @
e30607fd
...
...
@@ -205,6 +205,13 @@ struct dwrite_colorglyphenum {
#define GLYPH_BLOCK_MASK (GLYPH_BLOCK_SIZE - 1)
#define GLYPH_MAX 65536
enum
fontface_flags
{
FONTFACE_IS_SYMBOL
=
1
<<
0
,
FONTFACE_IS_MONOSPACED
=
1
<<
1
,
FONTFACE_HAS_KERN_PAIRS
=
1
<<
2
,
FONTFACE_HAS_VERTICAL_VARIANTS
=
1
<<
3
};
struct
dwrite_fontface
{
IDWriteFontFace3
IDWriteFontFace3_iface
;
LONG
ref
;
...
...
@@ -219,9 +226,7 @@ struct dwrite_fontface {
DWRITE_FONT_METRICS1
metrics
;
DWRITE_CARET_METRICS
caret
;
INT
charmap
;
BOOL
is_symbol
;
BOOL
has_kerning_pairs
:
1
;
BOOL
is_monospaced
:
1
;
UINT16
flags
;
struct
dwrite_fonttable
cmap
;
struct
dwrite_fonttable
vdmx
;
...
...
@@ -545,7 +550,7 @@ static BOOL WINAPI dwritefontface_IsSymbolFont(IDWriteFontFace3 *iface)
{
struct
dwrite_fontface
*
This
=
impl_from_IDWriteFontFace3
(
iface
);
TRACE
(
"(%p)
\n
"
,
This
);
return
This
->
is_symbol
;
return
!!
(
This
->
flags
&
FONTFACE_IS_SYMBOL
)
;
}
static
void
WINAPI
dwritefontface_GetMetrics
(
IDWriteFontFace3
*
iface
,
DWRITE_FONT_METRICS
*
metrics
)
...
...
@@ -859,7 +864,7 @@ static BOOL WINAPI dwritefontface1_IsMonospacedFont(IDWriteFontFace3 *iface)
{
struct
dwrite_fontface
*
This
=
impl_from_IDWriteFontFace3
(
iface
);
TRACE
(
"(%p)
\n
"
,
This
);
return
This
->
is_monospaced
;
return
!!
(
This
->
flags
&
FONTFACE_IS_MONOSPACED
)
;
}
static
HRESULT
WINAPI
dwritefontface1_GetDesignGlyphAdvances
(
IDWriteFontFace3
*
iface
,
...
...
@@ -929,7 +934,7 @@ static HRESULT WINAPI dwritefontface1_GetKerningPairAdjustments(IDWriteFontFace3
return
E_INVALIDARG
;
}
if
(
!
This
->
has_kerning_pairs
)
{
if
(
This
->
flags
&
FONTFACE_HAS_KERN_PAIRS
)
{
memset
(
adjustments
,
0
,
count
*
sizeof
(
INT32
));
return
S_OK
;
}
...
...
@@ -945,7 +950,7 @@ static BOOL WINAPI dwritefontface1_HasKerningPairs(IDWriteFontFace3 *iface)
{
struct
dwrite_fontface
*
This
=
impl_from_IDWriteFontFace3
(
iface
);
TRACE
(
"(%p)
\n
"
,
This
);
return
This
->
has_kerning_pairs
;
return
!!
(
This
->
flags
&
FONTFACE_HAS_KERN_PAIRS
)
;
}
static
HRESULT
WINAPI
dwritefontface1_GetRecommendedRenderingMode
(
IDWriteFontFace3
*
iface
,
...
...
@@ -968,8 +973,8 @@ static HRESULT WINAPI dwritefontface1_GetVerticalGlyphVariants(IDWriteFontFace3
static
BOOL
WINAPI
dwritefontface1_HasVerticalGlyphVariants
(
IDWriteFontFace3
*
iface
)
{
struct
dwrite_fontface
*
This
=
impl_from_IDWriteFontFace3
(
iface
);
FIXME
(
"(%p): stub
\n
"
,
This
);
return
FALSE
;
TRACE
(
"(%p)
\n
"
,
This
);
return
!!
(
This
->
flags
&
FONTFACE_HAS_VERTICAL_VARIANTS
)
;
}
static
BOOL
WINAPI
dwritefontface2_IsColorFont
(
IDWriteFontFace3
*
iface
)
...
...
@@ -4130,6 +4135,7 @@ HRESULT create_fontface(const struct fontface_desc *desc, IDWriteFontFace3 **ret
struct
file_stream_desc
stream_desc
;
struct
dwrite_fontface
*
fontface
;
HRESULT
hr
=
S_OK
;
BOOL
is_symbol
;
int
i
;
*
ret
=
NULL
;
...
...
@@ -4188,9 +4194,17 @@ HRESULT create_fontface(const struct fontface_desc *desc, IDWriteFontFace3 **ret
fontface
->
caret
.
slopeRun
=
fontface
->
caret
.
slopeRise
/
3
;
}
}
fontface
->
charmap
=
freetype_get_charmap_index
(
&
fontface
->
IDWriteFontFace3_iface
,
&
fontface
->
is_symbol
);
fontface
->
has_kerning_pairs
=
freetype_has_kerning_pairs
(
&
fontface
->
IDWriteFontFace3_iface
);
fontface
->
is_monospaced
=
freetype_is_monospaced
(
&
fontface
->
IDWriteFontFace3_iface
);
fontface
->
flags
=
0
;
fontface
->
charmap
=
freetype_get_charmap_index
(
&
fontface
->
IDWriteFontFace3_iface
,
&
is_symbol
);
if
(
is_symbol
)
fontface
->
flags
|=
FONTFACE_IS_SYMBOL
;
if
(
freetype_has_kerning_pairs
(
&
fontface
->
IDWriteFontFace3_iface
))
fontface
->
flags
|=
FONTFACE_HAS_KERN_PAIRS
;
if
(
freetype_is_monospaced
(
&
fontface
->
IDWriteFontFace3_iface
))
fontface
->
flags
|=
FONTFACE_IS_MONOSPACED
;
if
(
opentype_has_vertical_variants
(
&
fontface
->
IDWriteFontFace3_iface
))
fontface
->
flags
|=
FONTFACE_HAS_VERTICAL_VARIANTS
;
/* Font properties are reused from font object when 'normal' face creation path is used:
collection -> family -> matching font -> fontface.
...
...
dlls/dwrite/opentype.c
View file @
e30607fd
...
...
@@ -334,6 +334,49 @@ enum OPENTYPE_PLATFORM_ID
OPENTYPE_PLATFORM_CUSTOM
};
typedef
struct
{
WORD
FeatureParams
;
WORD
LookupCount
;
WORD
LookupListIndex
[
1
];
}
OT_Feature
;
typedef
struct
{
WORD
LookupCount
;
WORD
Lookup
[
1
];
}
OT_LookupList
;
typedef
struct
{
WORD
LookupType
;
WORD
LookupFlag
;
WORD
SubTableCount
;
WORD
SubTable
[
1
];
}
OT_LookupTable
;
typedef
struct
{
WORD
SubstFormat
;
WORD
Coverage
;
WORD
DeltaGlyphID
;
}
GSUB_SingleSubstFormat1
;
typedef
struct
{
WORD
SubstFormat
;
WORD
Coverage
;
WORD
GlyphCount
;
WORD
Substitute
[
1
];
}
GSUB_SingleSubstFormat2
;
typedef
struct
{
WORD
SubstFormat
;
WORD
ExtensionLookupType
;
DWORD
ExtensionOffset
;
}
GSUB_ExtensionPosFormat1
;
enum
OPENTYPE_GPOS_LOOKUPS
{
OPENTYPE_GPOS_SINGLE_SUBST
=
1
,
OPENTYPE_GPOS_EXTENSION_SUBST
=
7
};
enum
TT_NAME_WINDOWS_ENCODING_ID
{
TT_NAME_WINDOWS_ENCODING_SYMBOL
=
0
,
...
...
@@ -1887,3 +1930,76 @@ HRESULT opentype_get_font_signature(struct file_stream_desc *stream_desc, FONTSI
return
hr
;
}
BOOL
opentype_has_vertical_variants
(
IDWriteFontFace3
*
fontface
)
{
const
OT_FeatureList
*
featurelist
;
const
OT_LookupList
*
lookup_list
;
BOOL
exists
=
FALSE
,
ret
=
FALSE
;
const
GPOS_GSUB_Header
*
header
;
const
void
*
data
;
void
*
context
;
UINT32
size
;
HRESULT
hr
;
UINT16
i
;
hr
=
IDWriteFontFace3_TryGetFontTable
(
fontface
,
MS_GSUB_TAG
,
&
data
,
&
size
,
&
context
,
&
exists
);
if
(
FAILED
(
hr
)
||
!
exists
)
return
FALSE
;
header
=
data
;
featurelist
=
(
OT_FeatureList
*
)((
BYTE
*
)
header
+
GET_BE_WORD
(
header
->
FeatureList
));
lookup_list
=
(
const
OT_LookupList
*
)((
BYTE
*
)
header
+
GET_BE_WORD
(
header
->
LookupList
));
for
(
i
=
0
;
i
<
GET_BE_WORD
(
featurelist
->
FeatureCount
);
i
++
)
{
if
(
*
(
UINT32
*
)
featurelist
->
FeatureRecord
[
i
].
FeatureTag
==
DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING
)
{
const
OT_Feature
*
feature
=
(
const
OT_Feature
*
)((
BYTE
*
)
featurelist
+
GET_BE_WORD
(
featurelist
->
FeatureRecord
[
i
].
Feature
));
UINT16
lookup_count
=
GET_BE_WORD
(
feature
->
LookupCount
),
index
,
count
,
type
;
const
GSUB_SingleSubstFormat2
*
subst2
;
const
OT_LookupTable
*
lookup_table
;
UINT32
offset
;
if
(
lookup_count
==
0
)
continue
;
/* check if lookup is empty */
index
=
GET_BE_WORD
(
feature
->
LookupListIndex
[
0
]);
lookup_table
=
(
const
OT_LookupTable
*
)((
BYTE
*
)
lookup_list
+
GET_BE_WORD
(
lookup_list
->
Lookup
[
index
]));
type
=
GET_BE_WORD
(
lookup_table
->
LookupType
);
if
(
type
!=
OPENTYPE_GPOS_SINGLE_SUBST
&&
type
!=
OPENTYPE_GPOS_EXTENSION_SUBST
)
continue
;
count
=
GET_BE_WORD
(
lookup_table
->
SubTableCount
);
if
(
count
==
0
)
continue
;
offset
=
GET_BE_WORD
(
lookup_table
->
SubTable
[
0
]);
if
(
type
==
OPENTYPE_GPOS_EXTENSION_SUBST
)
{
const
GSUB_ExtensionPosFormat1
*
ext
=
(
const
GSUB_ExtensionPosFormat1
*
)((
const
BYTE
*
)
lookup_table
+
offset
);
if
(
GET_BE_WORD
(
ext
->
SubstFormat
)
==
1
)
offset
+=
GET_BE_DWORD
(
ext
->
ExtensionOffset
);
else
FIXME
(
"Unhandled Extension Substitution Format %u
\n
"
,
GET_BE_WORD
(
ext
->
SubstFormat
));
}
subst2
=
(
const
GSUB_SingleSubstFormat2
*
)((
BYTE
*
)
lookup_table
+
offset
);
index
=
GET_BE_WORD
(
subst2
->
SubstFormat
);
if
(
index
==
1
)
FIXME
(
"Validate Single Substitution Format 1
\n
"
);
else
if
(
index
==
2
)
{
/* SimSun-ExtB has 0 glyph count for this substitution */
if
(
GET_BE_WORD
(
subst2
->
GlyphCount
)
>
0
)
{
ret
=
TRUE
;
break
;
}
}
else
WARN
(
"Unknown Single Substitution Format, %u
\n
"
,
index
);
}
}
IDWriteFontFace3_ReleaseFontTable
(
fontface
,
context
);
return
ret
;
}
dlls/dwrite/tests/font.c
View file @
e30607fd
...
...
@@ -40,6 +40,7 @@
#define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d')
#define MS_HHEA_TAG DWRITE_MAKE_OPENTYPE_TAG('h','h','e','a')
#define MS_POST_TAG DWRITE_MAKE_OPENTYPE_TAG('p','o','s','t')
#define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B')
#ifdef WORDS_BIGENDIAN
#define GET_BE_WORD(x) (x)
...
...
@@ -220,6 +221,60 @@ typedef struct {
USHORT
numberOfHMetrics
;
}
TT_HHEA
;
typedef
struct
{
DWORD
version
;
WORD
ScriptList
;
WORD
FeatureList
;
WORD
LookupList
;
}
GSUB_Header
;
typedef
struct
{
CHAR
FeatureTag
[
4
];
WORD
Feature
;
}
OT_FeatureRecord
;
typedef
struct
{
WORD
FeatureCount
;
OT_FeatureRecord
FeatureRecord
[
1
];
}
OT_FeatureList
;
typedef
struct
{
WORD
FeatureParams
;
WORD
LookupCount
;
WORD
LookupListIndex
[
1
];
}
OT_Feature
;
typedef
struct
{
WORD
LookupCount
;
WORD
Lookup
[
1
];
}
OT_LookupList
;
typedef
struct
{
WORD
LookupType
;
WORD
LookupFlag
;
WORD
SubTableCount
;
WORD
SubTable
[
1
];
}
OT_LookupTable
;
typedef
struct
{
WORD
SubstFormat
;
WORD
Coverage
;
WORD
DeltaGlyphID
;
}
GSUB_SingleSubstFormat1
;
typedef
struct
{
WORD
SubstFormat
;
WORD
Coverage
;
WORD
GlyphCount
;
WORD
Substitute
[
1
];
}
GSUB_SingleSubstFormat2
;
typedef
struct
{
WORD
SubstFormat
;
WORD
ExtensionLookupType
;
DWORD
ExtensionOffset
;
}
GSUB_ExtensionPosFormat1
;
#include "poppack.h"
static
IDWriteFactory
*
create_factory
(
void
)
...
...
@@ -6300,6 +6355,153 @@ static void test_font_properties(void)
IDWriteFactory_Release
(
factory
);
}
static
BOOL
has_vertical_glyph_variants
(
IDWriteFontFace1
*
fontface
)
{
const
OT_FeatureList
*
featurelist
;
const
OT_LookupList
*
lookup_list
;
BOOL
exists
=
FALSE
,
ret
=
FALSE
;
const
GSUB_Header
*
header
;
const
void
*
data
;
void
*
context
;
UINT32
size
;
HRESULT
hr
;
UINT16
i
;
hr
=
IDWriteFontFace1_TryGetFontTable
(
fontface
,
MS_GSUB_TAG
,
&
data
,
&
size
,
&
context
,
&
exists
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
if
(
!
exists
)
return
FALSE
;
header
=
data
;
featurelist
=
(
OT_FeatureList
*
)((
BYTE
*
)
header
+
GET_BE_WORD
(
header
->
FeatureList
));
lookup_list
=
(
const
OT_LookupList
*
)((
BYTE
*
)
header
+
GET_BE_WORD
(
header
->
LookupList
));
for
(
i
=
0
;
i
<
GET_BE_WORD
(
featurelist
->
FeatureCount
);
i
++
)
{
if
(
*
(
UINT32
*
)
featurelist
->
FeatureRecord
[
i
].
FeatureTag
==
DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING
)
{
const
OT_Feature
*
feature
=
(
const
OT_Feature
*
)((
BYTE
*
)
featurelist
+
GET_BE_WORD
(
featurelist
->
FeatureRecord
[
i
].
Feature
));
UINT16
lookup_count
=
GET_BE_WORD
(
feature
->
LookupCount
),
index
,
count
,
type
;
const
GSUB_SingleSubstFormat2
*
subst2
;
const
OT_LookupTable
*
lookup_table
;
UINT32
offset
;
if
(
lookup_count
==
0
)
continue
;
ok
(
lookup_count
==
1
,
"got lookup count %u
\n
"
,
lookup_count
);
/* check if lookup is empty */
index
=
GET_BE_WORD
(
feature
->
LookupListIndex
[
0
]);
lookup_table
=
(
const
OT_LookupTable
*
)((
BYTE
*
)
lookup_list
+
GET_BE_WORD
(
lookup_list
->
Lookup
[
index
]));
type
=
GET_BE_WORD
(
lookup_table
->
LookupType
);
ok
(
type
==
1
||
type
==
7
,
"got unexpected lookup type %u
\n
"
,
type
);
count
=
GET_BE_WORD
(
lookup_table
->
SubTableCount
);
if
(
count
==
0
)
continue
;
ok
(
count
>
0
,
"got unexpected subtable count %u
\n
"
,
count
);
offset
=
GET_BE_WORD
(
lookup_table
->
SubTable
[
0
]);
if
(
type
==
7
)
{
const
GSUB_ExtensionPosFormat1
*
ext
=
(
const
GSUB_ExtensionPosFormat1
*
)((
const
BYTE
*
)
lookup_table
+
offset
);
if
(
GET_BE_WORD
(
ext
->
SubstFormat
)
==
1
)
offset
+=
GET_BE_DWORD
(
ext
->
ExtensionOffset
);
else
ok
(
0
,
"Unhandled Extension Substitution Format %u
\n
"
,
GET_BE_WORD
(
ext
->
SubstFormat
));
}
subst2
=
(
const
GSUB_SingleSubstFormat2
*
)((
BYTE
*
)
lookup_table
+
offset
);
index
=
GET_BE_WORD
(
subst2
->
SubstFormat
);
if
(
index
==
1
)
ok
(
0
,
"validate Single Substitution Format 1
\n
"
);
else
if
(
index
==
2
)
{
/* SimSun-ExtB has 0 glyph count for this substitution */
if
(
GET_BE_WORD
(
subst2
->
GlyphCount
)
>
0
)
{
ret
=
TRUE
;
break
;
}
}
else
ok
(
0
,
"unknown Single Substitution Format, %u
\n
"
,
index
);
}
}
IDWriteFontFace1_ReleaseFontTable
(
fontface
,
context
);
return
ret
;
}
static
void
test_HasVerticalGlyphVariants
(
void
)
{
IDWriteFontCollection
*
syscollection
;
IDWriteFontFace1
*
fontface1
;
IDWriteFontFace
*
fontface
;
IDWriteFactory
*
factory
;
UINT32
count
,
i
;
HRESULT
hr
;
factory
=
create_factory
();
fontface
=
create_fontface
(
factory
);
hr
=
IDWriteFontFace_QueryInterface
(
fontface
,
&
IID_IDWriteFontFace1
,
(
void
**
)
&
fontface1
);
IDWriteFontFace_Release
(
fontface
);
if
(
hr
!=
S_OK
)
{
win_skip
(
"HasVerticalGlyphVariants() is not supported.
\n
"
);
IDWriteFactory_Release
(
factory
);
return
;
}
IDWriteFontFace1_Release
(
fontface1
);
hr
=
IDWriteFactory_GetSystemFontCollection
(
factory
,
&
syscollection
,
FALSE
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
count
=
IDWriteFontCollection_GetFontFamilyCount
(
syscollection
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
IDWriteLocalizedStrings
*
names
;
BOOL
expected_vert
,
has_vert
;
IDWriteFontFamily
*
family
;
IDWriteFont
*
font
;
WCHAR
nameW
[
256
];
hr
=
IDWriteFontCollection_GetFontFamily
(
syscollection
,
i
,
&
family
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
hr
=
IDWriteFontFamily_GetFirstMatchingFont
(
family
,
DWRITE_FONT_WEIGHT_NORMAL
,
DWRITE_FONT_STRETCH_NORMAL
,
DWRITE_FONT_STYLE_NORMAL
,
&
font
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
hr
=
IDWriteFont_CreateFontFace
(
font
,
&
fontface
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
hr
=
IDWriteFontFace_QueryInterface
(
fontface
,
&
IID_IDWriteFontFace1
,
(
void
**
)
&
fontface1
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
hr
=
IDWriteFontFamily_GetFamilyNames
(
family
,
&
names
);
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
get_enus_string
(
names
,
nameW
,
sizeof
(
nameW
)
/
sizeof
(
nameW
[
0
]));
expected_vert
=
has_vertical_glyph_variants
(
fontface1
);
has_vert
=
IDWriteFontFace1_HasVerticalGlyphVariants
(
fontface1
);
ok
(
expected_vert
==
has_vert
,
"%s: expected vertical feature %d, got %d
\n
"
,
wine_dbgstr_w
(
nameW
),
expected_vert
,
has_vert
);
IDWriteLocalizedStrings_Release
(
names
);
IDWriteFont_Release
(
font
);
IDWriteFontFace1_Release
(
fontface1
);
IDWriteFontFace_Release
(
fontface
);
IDWriteFontFamily_Release
(
family
);
}
IDWriteFontCollection_Release
(
syscollection
);
IDWriteFactory_Release
(
factory
);
}
START_TEST
(
font
)
{
IDWriteFactory
*
factory
;
...
...
@@ -6357,6 +6559,7 @@ START_TEST(font)
test_CreateFontFaceReference
();
test_GetFontSignature
();
test_font_properties
();
test_HasVerticalGlyphVariants
();
IDWriteFactory_Release
(
factory
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment