mirror of https://github.com/OpenTTD/OpenTTD
(svn r25502) [1.3] -Backport from trunk:
- Fix: Proper support for Brahmic scripts (e.g. Tamil and Thai) [FS#5481] (r25501, r25493, r25485, r25483, r25482, r25481, r25478, r25477, r25476, r25474, r25473, r25472, r25471, r25470, r25469, r25468, r25467, r25466, r25465, r25463, r25462, r25455, r25452, r25451, r25450, r25447, r25446, r25445, r25444, r25443, r25442, r25441, r25440, r25439, r25438, r25437, r25436, r25343, r25157)release/1.3
parent
79c02e4ae4
commit
7e74941a73
|
@ -1718,9 +1718,9 @@ make_cflags_and_ldflags() {
|
|||
|
||||
# Some icu-configs have the 'feature' of not adding a space where others do add the space
|
||||
if [ "$static_icu" != "0" ]; then
|
||||
LIBS="$LIBS `$icu_config --ldflags-searchpath` `$icu_config --ldflags-libsonly | tr '\n\r' ' ' | sed s/licu/lsicu/g`"
|
||||
LIBS="$LIBS `$icu_config --ldflags-searchpath` `$icu_config --ldflags-libsonly --ldflags-layout | tr '\n\r' ' ' | sed s/licu/lsicu/g`"
|
||||
else
|
||||
LIBS="$LIBS `$icu_config --ldflags-searchpath` `$icu_config --ldflags-libsonly | tr '\n\r' ' '`"
|
||||
LIBS="$LIBS `$icu_config --ldflags-searchpath` `$icu_config --ldflags-libsonly --ldflags-layout | tr '\n\r' ' '`"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -176,7 +176,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -233,7 +233,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -280,7 +280,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -317,11 +317,13 @@
|
|||
<ClCompile Include="..\src\fileio.cpp" />
|
||||
<ClCompile Include="..\src\fios.cpp" />
|
||||
<ClCompile Include="..\src\fontcache.cpp" />
|
||||
<ClCompile Include="..\src\fontdetection.cpp" />
|
||||
<ClCompile Include="..\src\base_consist.cpp" />
|
||||
<ClCompile Include="..\src\gamelog.cpp" />
|
||||
<ClCompile Include="..\src\genworld.cpp" />
|
||||
<ClCompile Include="..\src\gfx.cpp" />
|
||||
<ClCompile Include="..\src\gfxinit.cpp" />
|
||||
<ClCompile Include="..\src\gfx_layout.cpp" />
|
||||
<ClCompile Include="..\src\goal.cpp" />
|
||||
<ClCompile Include="..\src\ground_vehicle.cpp" />
|
||||
<ClCompile Include="..\src\heightmap.cpp" />
|
||||
|
@ -440,11 +442,13 @@
|
|||
<ClInclude Include="..\src\fileio_type.h" />
|
||||
<ClInclude Include="..\src\fios.h" />
|
||||
<ClInclude Include="..\src\fontcache.h" />
|
||||
<ClInclude Include="..\src\fontdetection.h" />
|
||||
<ClInclude Include="..\src\base_consist.h" />
|
||||
<ClInclude Include="..\src\gamelog.h" />
|
||||
<ClInclude Include="..\src\gamelog_internal.h" />
|
||||
<ClInclude Include="..\src\genworld.h" />
|
||||
<ClInclude Include="..\src\gfx_func.h" />
|
||||
<ClInclude Include="..\src\gfx_layout.h" />
|
||||
<ClInclude Include="..\src\gfx_type.h" />
|
||||
<ClInclude Include="..\src\gfxinit.h" />
|
||||
<ClInclude Include="..\src\goal_base.h" />
|
||||
|
|
|
@ -180,6 +180,9 @@
|
|||
<ClCompile Include="..\src\fontcache.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\fontdetection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\base_consist.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -195,6 +198,9 @@
|
|||
<ClCompile Include="..\src\gfxinit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\gfx_layout.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\goal.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -549,6 +555,9 @@
|
|||
<ClInclude Include="..\src\fontcache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\fontdetection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\base_consist.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -564,6 +573,9 @@
|
|||
<ClInclude Include="..\src\gfx_func.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\gfx_layout.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\gfx_type.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -176,7 +176,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -233,7 +233,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
@ -280,7 +280,7 @@
|
|||
<Culture>0x0809</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -184,7 +184,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
@ -292,7 +292,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -392,7 +392,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
@ -538,6 +538,10 @@
|
|||
RelativePath=".\..\src\fontcache.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\fontdetection.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\base_consist.cpp"
|
||||
>
|
||||
|
@ -558,6 +562,10 @@
|
|||
RelativePath=".\..\src\gfxinit.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\gfx_layout.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\goal.cpp"
|
||||
>
|
||||
|
@ -1034,6 +1042,10 @@
|
|||
RelativePath=".\..\src\fontcache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\fontdetection.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\base_consist.h"
|
||||
>
|
||||
|
@ -1054,6 +1066,10 @@
|
|||
RelativePath=".\..\src\gfx_func.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\gfx_layout.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\gfx_type.h"
|
||||
>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -184,7 +184,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
@ -292,7 +292,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -392,7 +392,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -183,7 +183,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
@ -290,7 +290,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -389,7 +389,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
@ -535,6 +535,10 @@
|
|||
RelativePath=".\..\src\fontcache.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\fontdetection.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\base_consist.cpp"
|
||||
>
|
||||
|
@ -555,6 +559,10 @@
|
|||
RelativePath=".\..\src\gfxinit.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\gfx_layout.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\goal.cpp"
|
||||
>
|
||||
|
@ -1031,6 +1039,10 @@
|
|||
RelativePath=".\..\src\fontcache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\fontdetection.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\base_consist.h"
|
||||
>
|
||||
|
@ -1051,6 +1063,10 @@
|
|||
RelativePath=".\..\src\gfx_func.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\gfx_layout.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\gfx_type.h"
|
||||
>
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -183,7 +183,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
@ -290,7 +290,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
|
@ -389,7 +389,7 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib"
|
||||
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
|
||||
LinkIncremental="0"
|
||||
SuppressStartupBanner="true"
|
||||
IgnoreDefaultLibraryNames="LIBCMT.lib"
|
||||
|
|
|
@ -25,11 +25,13 @@ engine.cpp
|
|||
fileio.cpp
|
||||
fios.cpp
|
||||
fontcache.cpp
|
||||
fontdetection.cpp
|
||||
base_consist.cpp
|
||||
gamelog.cpp
|
||||
genworld.cpp
|
||||
gfx.cpp
|
||||
gfxinit.cpp
|
||||
gfx_layout.cpp
|
||||
goal.cpp
|
||||
ground_vehicle.cpp
|
||||
heightmap.cpp
|
||||
|
@ -173,11 +175,13 @@ fileio_func.h
|
|||
fileio_type.h
|
||||
fios.h
|
||||
fontcache.h
|
||||
fontdetection.h
|
||||
base_consist.h
|
||||
gamelog.h
|
||||
gamelog_internal.h
|
||||
genworld.h
|
||||
gfx_func.h
|
||||
gfx_layout.h
|
||||
gfx_type.h
|
||||
gfxinit.h
|
||||
goal_base.h
|
||||
|
|
|
@ -526,7 +526,7 @@ public:
|
|||
|
||||
uint Height(uint width) const
|
||||
{
|
||||
return max(FONT_HEIGHT_NORMAL, (byte)14);
|
||||
return max(FONT_HEIGHT_NORMAL, 14);
|
||||
}
|
||||
|
||||
bool Selectable() const
|
||||
|
|
1485
src/fontcache.cpp
1485
src/fontcache.cpp
File diff suppressed because it is too large
Load Diff
242
src/fontcache.h
242
src/fontcache.h
|
@ -12,84 +12,200 @@
|
|||
#ifndef FONTCACHE_H
|
||||
#define FONTCACHE_H
|
||||
|
||||
#include "string_type.h"
|
||||
#include "spritecache.h"
|
||||
|
||||
/** Glyphs are characters from a font. */
|
||||
typedef uint32 GlyphID;
|
||||
static const GlyphID SPRITE_GLYPH = 1U << 30;
|
||||
|
||||
/** Font cache for basic fonts. */
|
||||
class FontCache {
|
||||
private:
|
||||
static FontCache *caches[FS_END]; ///< All the font caches.
|
||||
protected:
|
||||
FontCache *parent; ///< The parent of this font cache.
|
||||
const FontSize fs; ///< The size of the font.
|
||||
int height; ///< The height of the font.
|
||||
int ascender; ///< The ascender value of the font.
|
||||
int descender; ///< The descender value of the font.
|
||||
int units_per_em; ///< The units per EM value of the font.
|
||||
public:
|
||||
FontCache(FontSize fs);
|
||||
virtual ~FontCache();
|
||||
|
||||
/**
|
||||
* Get the FontSize of the font.
|
||||
* @return The FontSize.
|
||||
*/
|
||||
inline FontSize GetSize() const { return this->fs; }
|
||||
|
||||
/**
|
||||
* Get the height of the font.
|
||||
* @return The height of the font.
|
||||
*/
|
||||
inline int GetHeight() const { return this->height; }
|
||||
|
||||
/**
|
||||
* Get the ascender value of the font.
|
||||
* @return The ascender value of the font.
|
||||
*/
|
||||
inline int GetAscender() const { return this->ascender; }
|
||||
|
||||
/**
|
||||
* Get the descender value of the font.
|
||||
* @return The descender value of the font.
|
||||
*/
|
||||
inline int GetDescender() const{ return this->descender; }
|
||||
|
||||
/**
|
||||
* Get the units per EM value of the font.
|
||||
* @return The units per EM value of the font.
|
||||
*/
|
||||
inline int GetUnitsPerEM() const { return this->units_per_em; }
|
||||
|
||||
/**
|
||||
* Get the SpriteID mapped to the given key
|
||||
* @param key The key to get the sprite for.
|
||||
* @return The sprite.
|
||||
*/
|
||||
virtual SpriteID GetUnicodeGlyph(WChar key) = 0;
|
||||
|
||||
/**
|
||||
* Map a SpriteID to the key
|
||||
* @param key The key to map to.
|
||||
* @param sprite The sprite that is being mapped.
|
||||
*/
|
||||
virtual void SetUnicodeGlyph(WChar key, SpriteID sprite) = 0;
|
||||
|
||||
/** Initialize the glyph map */
|
||||
virtual void InitializeUnicodeGlyphMap() = 0;
|
||||
|
||||
/** Clear the font cache. */
|
||||
virtual void ClearFontCache() = 0;
|
||||
|
||||
/**
|
||||
* Get the glyph (sprite) of the given key.
|
||||
* @param key The key to look up.
|
||||
* @return The sprite.
|
||||
*/
|
||||
virtual const Sprite *GetGlyph(GlyphID key) = 0;
|
||||
|
||||
/**
|
||||
* Get the width of the glyph with the given key.
|
||||
* @param key The key to look up.
|
||||
* @return The width.
|
||||
*/
|
||||
virtual uint GetGlyphWidth(GlyphID key) = 0;
|
||||
|
||||
/**
|
||||
* Do we need to draw a glyph shadow?
|
||||
* @return True if it has to be done, otherwise false.
|
||||
*/
|
||||
virtual bool GetDrawGlyphShadow() = 0;
|
||||
|
||||
/**
|
||||
* Map a character into a glyph.
|
||||
* @param key The character.
|
||||
* @return The glyph ID used to draw the character.
|
||||
*/
|
||||
virtual GlyphID MapCharToGlyph(WChar key) = 0;
|
||||
|
||||
/**
|
||||
* Read a font table from the font.
|
||||
* @param tag The of the table to load.
|
||||
* @param length The length of the read data.
|
||||
* @return The loaded table data.
|
||||
*/
|
||||
virtual const void *GetFontTable(uint32 tag, size_t &length) = 0;
|
||||
|
||||
/**
|
||||
* Get the font cache of a given font size.
|
||||
* @param fs The font size to look up.
|
||||
* @return The font cache.
|
||||
*/
|
||||
static inline FontCache *Get(FontSize fs)
|
||||
{
|
||||
assert(fs < FS_END);
|
||||
return FontCache::caches[fs];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the font cache has a parent.
|
||||
*/
|
||||
inline bool HasParent()
|
||||
{
|
||||
return this->parent != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
/** Get the SpriteID mapped to the given font size and key */
|
||||
SpriteID GetUnicodeGlyph(FontSize size, uint32 key);
|
||||
static inline SpriteID GetUnicodeGlyph(FontSize size, WChar key)
|
||||
{
|
||||
return FontCache::Get(size)->GetUnicodeGlyph(key);
|
||||
}
|
||||
|
||||
/** Map a SpriteID to the font size and key */
|
||||
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite);
|
||||
static inline void SetUnicodeGlyph(FontSize size, WChar key, SpriteID sprite)
|
||||
{
|
||||
FontCache::Get(size)->SetUnicodeGlyph(key, sprite);
|
||||
}
|
||||
|
||||
/** Initialize the glyph map */
|
||||
void InitializeUnicodeGlyphMap();
|
||||
static inline void InitializeUnicodeGlyphMap()
|
||||
{
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
FontCache::Get(fs)->InitializeUnicodeGlyphMap();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ClearFontCache() {
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
FontCache::Get(fs)->ClearFontCache();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the Sprite for a glyph */
|
||||
static inline const Sprite *GetGlyph(FontSize size, WChar key)
|
||||
{
|
||||
FontCache *fc = FontCache::Get(size);
|
||||
return fc->GetGlyph(fc->MapCharToGlyph(key));
|
||||
}
|
||||
|
||||
/** Get the width of a glyph */
|
||||
static inline uint GetGlyphWidth(FontSize size, WChar key)
|
||||
{
|
||||
FontCache *fc = FontCache::Get(size);
|
||||
return fc->GetGlyphWidth(fc->MapCharToGlyph(key));
|
||||
}
|
||||
|
||||
static inline bool GetDrawGlyphShadow(FontSize size)
|
||||
{
|
||||
return FontCache::Get(size)->GetDrawGlyphShadow();
|
||||
}
|
||||
|
||||
#ifdef WITH_FREETYPE
|
||||
|
||||
/** Settings for a single freetype font. */
|
||||
struct FreeTypeSubSetting {
|
||||
char font[MAX_PATH]; ///< The name of the font, or path to the font.
|
||||
uint size; ///< The (requested) size of the font.
|
||||
bool aa; ///< Whether to do anti aliasing or not.
|
||||
};
|
||||
|
||||
/** Settings for the freetype fonts. */
|
||||
struct FreeTypeSettings {
|
||||
char small_font[MAX_PATH];
|
||||
char medium_font[MAX_PATH];
|
||||
char large_font[MAX_PATH];
|
||||
char mono_font[MAX_PATH];
|
||||
uint small_size;
|
||||
uint medium_size;
|
||||
uint large_size;
|
||||
uint mono_size;
|
||||
bool small_aa;
|
||||
bool medium_aa;
|
||||
bool large_aa;
|
||||
bool mono_aa;
|
||||
FreeTypeSubSetting small; ///< The smallest font; mostly used for zoomed out view.
|
||||
FreeTypeSubSetting medium; ///< The normal font size.
|
||||
FreeTypeSubSetting large; ///< The largest font; mostly used for newspapers.
|
||||
FreeTypeSubSetting mono; ///< The mono space font used for license/readme viewers.
|
||||
};
|
||||
|
||||
extern FreeTypeSettings _freetype;
|
||||
|
||||
void InitFreeType(bool monospace);
|
||||
void UninitFreeType();
|
||||
void ClearFontCache();
|
||||
const Sprite *GetGlyph(FontSize size, uint32 key);
|
||||
uint GetGlyphWidth(FontSize size, uint32 key);
|
||||
bool GetDrawGlyphShadow();
|
||||
|
||||
/**
|
||||
* We would like to have a fallback font as the current one
|
||||
* doesn't contain all characters we need.
|
||||
* This function must set all fonts of settings.
|
||||
* @param settings the settings to overwrite the fontname of.
|
||||
* @param language_isocode the language, e.g. en_GB.
|
||||
* @param winlangid the language ID windows style.
|
||||
* @param callback The function to call to check for missing glyphs.
|
||||
* @return true if a font has been set, false otherwise.
|
||||
*/
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, class MissingGlyphSearcher *callback);
|
||||
|
||||
#else
|
||||
|
||||
/* Stub for initializiation */
|
||||
static inline void InitFreeType(bool monospace) { extern void ResetFontSizes(bool monospace); ResetFontSizes(monospace); }
|
||||
static inline void UninitFreeType() {}
|
||||
static inline void ClearFontCache() {}
|
||||
|
||||
/** Get the Sprite for a glyph */
|
||||
static inline const Sprite *GetGlyph(FontSize size, uint32 key)
|
||||
{
|
||||
SpriteID sprite = GetUnicodeGlyph(size, key);
|
||||
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
|
||||
return GetSprite(sprite, ST_FONT);
|
||||
}
|
||||
|
||||
|
||||
/** Get the width of a glyph */
|
||||
static inline uint GetGlyphWidth(FontSize size, uint32 key)
|
||||
{
|
||||
SpriteID sprite = GetUnicodeGlyph(size, key);
|
||||
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
|
||||
return SpriteExists(sprite) ? GetSprite(sprite, ST_FONT)->width + (size != FS_NORMAL) : 0;
|
||||
}
|
||||
|
||||
static inline bool GetDrawGlyphShadow()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* WITH_FREETYPE */
|
||||
|
||||
void InitFreeType(bool monospace);
|
||||
void UninitFreeType();
|
||||
|
||||
#endif /* FONTCACHE_H */
|
||||
|
|
|
@ -0,0 +1,780 @@
|
|||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file fontdetection.cpp Detection of the right font. */
|
||||
|
||||
#ifdef WITH_FREETYPE
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "debug.h"
|
||||
#include "fontdetection.h"
|
||||
#include "string_func.h"
|
||||
#include "strings_func.h"
|
||||
|
||||
extern FT_Library _library;
|
||||
|
||||
/**
|
||||
* Get the font loaded into a Freetype face by using a font-name.
|
||||
* If no appropriate font is found, the function returns an error
|
||||
*/
|
||||
|
||||
/* ========================================================================================
|
||||
* Windows support
|
||||
* ======================================================================================== */
|
||||
|
||||
#ifdef WIN32
|
||||
#include "core/alloc_func.hpp"
|
||||
#include "core/math_func.hpp"
|
||||
#include <windows.h>
|
||||
#include <shlobj.h> /* SHGetFolderPath */
|
||||
#include "os/windows/win32.h"
|
||||
|
||||
/**
|
||||
* Get the short DOS 8.3 format for paths.
|
||||
* FreeType doesn't support Unicode filenames and Windows' fopen (as used
|
||||
* by FreeType) doesn't support UTF-8 filenames. So we have to convert the
|
||||
* filename into something that isn't UTF-8 but represents the Unicode file
|
||||
* name. This is the short DOS 8.3 format. This does not contain any
|
||||
* characters that fopen doesn't support.
|
||||
* @param long_path the path in UTF-8.
|
||||
* @return the short path in ANSI (ASCII).
|
||||
*/
|
||||
char *GetShortPath(const char *long_path)
|
||||
{
|
||||
static char short_path[MAX_PATH];
|
||||
#ifdef UNICODE
|
||||
/* The non-unicode GetShortPath doesn't support UTF-8...,
|
||||
* so convert the path to wide chars, then get the short
|
||||
* path and convert it back again. */
|
||||
wchar_t long_path_w[MAX_PATH];
|
||||
MultiByteToWideChar(CP_UTF8, 0, long_path, -1, long_path_w, MAX_PATH);
|
||||
|
||||
wchar_t short_path_w[MAX_PATH];
|
||||
GetShortPathNameW(long_path_w, short_path_w, MAX_PATH);
|
||||
|
||||
WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, MAX_PATH, NULL, NULL);
|
||||
#else
|
||||
/* Technically not needed, but do it for consistency. */
|
||||
GetShortPathNameA(long_path, short_path, MAX_PATH);
|
||||
#endif
|
||||
return short_path;
|
||||
}
|
||||
|
||||
/* Get the font file to be loaded into Freetype by looping the registry
|
||||
* location where windows lists all installed fonts. Not very nice, will
|
||||
* surely break if the registry path changes, but it works. Much better
|
||||
* solution would be to use CreateFont, and extract the font data from it
|
||||
* by GetFontData. The problem with this is that the font file needs to be
|
||||
* kept in memory then until the font is no longer needed. This could mean
|
||||
* an additional memory usage of 30MB (just for fonts!) when using an eastern
|
||||
* font for all font sizes */
|
||||
#define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
|
||||
#define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
|
||||
{
|
||||
FT_Error err = FT_Err_Cannot_Open_Resource;
|
||||
HKEY hKey;
|
||||
LONG ret;
|
||||
TCHAR vbuffer[MAX_PATH], dbuffer[256];
|
||||
TCHAR *font_namep;
|
||||
char *font_path;
|
||||
uint index;
|
||||
|
||||
/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
|
||||
* "Windows NT" key, on Windows 9x in the Windows key. To save us having
|
||||
* to retrieve the windows version, we'll just query both */
|
||||
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
|
||||
if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
|
||||
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* For Unicode we need some conversion between widechar and
|
||||
* normal char to match the data returned by RegEnumValue,
|
||||
* otherwise just use parameter */
|
||||
#if defined(UNICODE)
|
||||
font_namep = MallocT<TCHAR>(MAX_PATH);
|
||||
MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
|
||||
#else
|
||||
font_namep = const_cast<char *>(font_name); // only cast because in unicode pointer is not const
|
||||
#endif
|
||||
|
||||
for (index = 0;; index++) {
|
||||
TCHAR *s;
|
||||
DWORD vbuflen = lengthof(vbuffer);
|
||||
DWORD dbuflen = lengthof(dbuffer);
|
||||
|
||||
ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
|
||||
if (ret != ERROR_SUCCESS) goto registry_no_font_found;
|
||||
|
||||
/* The font names in the registry are of the following 3 forms:
|
||||
* - ADMUI3.fon
|
||||
* - Book Antiqua Bold (TrueType)
|
||||
* - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
|
||||
* We will strip the font-type '()' if any and work with the font name
|
||||
* itself, which must match exactly; if...
|
||||
* TTC files, font files which contain more than one font are separated
|
||||
* by '&'. Our best bet will be to do substr match for the fontname
|
||||
* and then let FreeType figure out which index to load */
|
||||
s = _tcschr(vbuffer, _T('('));
|
||||
if (s != NULL) s[-1] = '\0';
|
||||
|
||||
if (_tcschr(vbuffer, _T('&')) == NULL) {
|
||||
if (_tcsicmp(vbuffer, font_namep) == 0) break;
|
||||
} else {
|
||||
if (_tcsstr(vbuffer, font_namep) != NULL) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
|
||||
DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
|
||||
goto folder_error;
|
||||
}
|
||||
|
||||
/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
|
||||
* contain multiple fonts inside this single file. GetFontData however
|
||||
* returns the whole file, so we need to check each font inside to get the
|
||||
* proper font.
|
||||
* Also note that FreeType does not support UNICODE filenames! */
|
||||
#if defined(UNICODE)
|
||||
/* We need a cast here back from wide because FreeType doesn't support
|
||||
* widechar filenames. Just use the buffer we allocated before for the
|
||||
* font_name search */
|
||||
font_path = (char*)font_namep;
|
||||
WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR));
|
||||
#else
|
||||
font_path = vbuffer;
|
||||
#endif
|
||||
|
||||
ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR));
|
||||
ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR));
|
||||
|
||||
/* Convert the path into something that FreeType understands */
|
||||
font_path = GetShortPath(font_path);
|
||||
|
||||
index = 0;
|
||||
do {
|
||||
err = FT_New_Face(_library, font_path, index, face);
|
||||
if (err != FT_Err_Ok) break;
|
||||
|
||||
if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
|
||||
/* Try english name if font name failed */
|
||||
if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
|
||||
err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
} while ((FT_Long)++index != (*face)->num_faces);
|
||||
|
||||
|
||||
folder_error:
|
||||
registry_no_font_found:
|
||||
#if defined(UNICODE)
|
||||
free(font_namep);
|
||||
#endif
|
||||
RegCloseKey(hKey);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonts can have localised names and when the system locale is the same as
|
||||
* one of those localised names Windows will always return that localised name
|
||||
* instead of allowing to get the non-localised (English US) name of the font.
|
||||
* This will later on give problems as freetype uses the non-localised name of
|
||||
* the font and we need to compare based on that name.
|
||||
* Windows furthermore DOES NOT have an API to get the non-localised name nor
|
||||
* can we override the system locale. This means that we have to actually read
|
||||
* the font itself to gather the font name we want.
|
||||
* Based on: http://blogs.msdn.com/michkap/archive/2006/02/13/530814.aspx
|
||||
* @param logfont the font information to get the english name of.
|
||||
* @return the English name (if it could be found).
|
||||
*/
|
||||
static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
|
||||
{
|
||||
static char font_name[MAX_PATH];
|
||||
const char *ret_font_name = NULL;
|
||||
uint pos = 0;
|
||||
HDC dc;
|
||||
HGDIOBJ oldfont;
|
||||
byte *buf;
|
||||
DWORD dw;
|
||||
uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
|
||||
|
||||
HFONT font = CreateFontIndirect(&logfont->elfLogFont);
|
||||
if (font == NULL) goto err1;
|
||||
|
||||
dc = GetDC(NULL);
|
||||
oldfont = SelectObject(dc, font);
|
||||
dw = GetFontData(dc, 'eman', 0, NULL, 0);
|
||||
if (dw == GDI_ERROR) goto err2;
|
||||
|
||||
buf = MallocT<byte>(dw);
|
||||
dw = GetFontData(dc, 'eman', 0, buf, dw);
|
||||
if (dw == GDI_ERROR) goto err3;
|
||||
|
||||
format = buf[pos++] << 8;
|
||||
format += buf[pos++];
|
||||
assert(format == 0);
|
||||
count = buf[pos++] << 8;
|
||||
count += buf[pos++];
|
||||
stringOffset = buf[pos++] << 8;
|
||||
stringOffset += buf[pos++];
|
||||
for (uint i = 0; i < count; i++) {
|
||||
platformId = buf[pos++] << 8;
|
||||
platformId += buf[pos++];
|
||||
encodingId = buf[pos++] << 8;
|
||||
encodingId += buf[pos++];
|
||||
languageId = buf[pos++] << 8;
|
||||
languageId += buf[pos++];
|
||||
nameId = buf[pos++] << 8;
|
||||
nameId += buf[pos++];
|
||||
if (nameId != 1) {
|
||||
pos += 4; // skip length and offset
|
||||
continue;
|
||||
}
|
||||
length = buf[pos++] << 8;
|
||||
length += buf[pos++];
|
||||
offset = buf[pos++] << 8;
|
||||
offset += buf[pos++];
|
||||
|
||||
/* Don't buffer overflow */
|
||||
length = min(length, MAX_PATH - 1);
|
||||
for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
|
||||
font_name[length] = '\0';
|
||||
|
||||
if ((platformId == 1 && languageId == 0) || // Macintosh English
|
||||
(platformId == 3 && languageId == 0x0409)) { // Microsoft English (US)
|
||||
ret_font_name = font_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err3:
|
||||
free(buf);
|
||||
err2:
|
||||
SelectObject(dc, oldfont);
|
||||
ReleaseDC(NULL, dc);
|
||||
DeleteObject(font);
|
||||
err1:
|
||||
return ret_font_name == NULL ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name;
|
||||
}
|
||||
|
||||
class FontList {
|
||||
protected:
|
||||
TCHAR **fonts;
|
||||
uint items;
|
||||
uint capacity;
|
||||
|
||||
public:
|
||||
FontList() : fonts(NULL), items(0), capacity(0) { };
|
||||
|
||||
~FontList() {
|
||||
if (this->fonts == NULL) return;
|
||||
|
||||
for (uint i = 0; i < this->items; i++) {
|
||||
free(this->fonts[i]);
|
||||
}
|
||||
|
||||
free(this->fonts);
|
||||
}
|
||||
|
||||
bool Add(const TCHAR *font) {
|
||||
for (uint i = 0; i < this->items; i++) {
|
||||
if (_tcscmp(this->fonts[i], font) == 0) return false;
|
||||
}
|
||||
|
||||
if (this->items == this->capacity) {
|
||||
this->capacity += 10;
|
||||
this->fonts = ReallocT(this->fonts, this->capacity);
|
||||
}
|
||||
|
||||
this->fonts[this->items++] = _tcsdup(font);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct EFCParam {
|
||||
FreeTypeSettings *settings;
|
||||
LOCALESIGNATURE locale;
|
||||
MissingGlyphSearcher *callback;
|
||||
FontList fonts;
|
||||
};
|
||||
|
||||
static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
|
||||
{
|
||||
EFCParam *info = (EFCParam *)lParam;
|
||||
|
||||
/* Skip duplicates */
|
||||
if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1;
|
||||
/* Only use TrueType fonts */
|
||||
if (!(type & TRUETYPE_FONTTYPE)) return 1;
|
||||
/* Don't use SYMBOL fonts */
|
||||
if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
|
||||
/* Use monospaced fonts when asked for it. */
|
||||
if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
|
||||
|
||||
/* The font has to have at least one of the supported locales to be usable. */
|
||||
if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
|
||||
/* On win9x metric->ntmFontSig seems to contain garbage. */
|
||||
FONTSIGNATURE fs;
|
||||
memset(&fs, 0, sizeof(fs));
|
||||
HFONT font = CreateFontIndirect(&logfont->elfLogFont);
|
||||
if (font != NULL) {
|
||||
HDC dc = GetDC(NULL);
|
||||
HGDIOBJ oldfont = SelectObject(dc, font);
|
||||
GetTextCharsetInfo(dc, &fs, 0);
|
||||
SelectObject(dc, oldfont);
|
||||
ReleaseDC(NULL, dc);
|
||||
DeleteObject(font);
|
||||
}
|
||||
if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
|
||||
}
|
||||
|
||||
char font_name[MAX_PATH];
|
||||
#if defined(UNICODE)
|
||||
WIDE_TO_MB_BUFFER((const TCHAR*)logfont->elfFullName, font_name, lengthof(font_name));
|
||||
#else
|
||||
strecpy(font_name, (const TCHAR*)logfont->elfFullName, lastof(font_name));
|
||||
#endif
|
||||
|
||||
/* Add english name after font name */
|
||||
const char *english_name = GetEnglishFontName(logfont);
|
||||
strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name));
|
||||
|
||||
/* Check whether we can actually load the font. */
|
||||
bool ft_init = _library != NULL;
|
||||
bool found = false;
|
||||
FT_Face face;
|
||||
/* Init FreeType if needed. */
|
||||
if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
|
||||
FT_Done_Face(face);
|
||||
found = true;
|
||||
}
|
||||
if (!ft_init) {
|
||||
/* Uninit FreeType if we did the init. */
|
||||
FT_Done_FreeType(_library);
|
||||
_library = NULL;
|
||||
}
|
||||
|
||||
if (!found) return 1;
|
||||
|
||||
info->callback->SetFontNames(info->settings, font_name);
|
||||
if (info->callback->FindMissingGlyphs(NULL)) return 1;
|
||||
DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
|
||||
return 0; // stop enumerating
|
||||
}
|
||||
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
|
||||
{
|
||||
DEBUG(freetype, 1, "Trying fallback fonts");
|
||||
EFCParam langInfo;
|
||||
if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
|
||||
/* Invalid langid or some other mysterious error, can't determine fallback font. */
|
||||
DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
|
||||
return false;
|
||||
}
|
||||
langInfo.settings = settings;
|
||||
langInfo.callback = callback;
|
||||
|
||||
LOGFONT font;
|
||||
/* Enumerate all fonts. */
|
||||
font.lfCharSet = DEFAULT_CHARSET;
|
||||
font.lfFaceName[0] = '\0';
|
||||
font.lfPitchAndFamily = 0;
|
||||
|
||||
HDC dc = GetDC(NULL);
|
||||
int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
|
||||
ReleaseDC(NULL, dc);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__) /* end ifdef Win32 */
|
||||
/* ========================================================================================
|
||||
* OSX support
|
||||
* ======================================================================================== */
|
||||
|
||||
#include "os/macosx/macos.h"
|
||||
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
|
||||
{
|
||||
FT_Error err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
/* Get font reference from name. */
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
|
||||
ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
|
||||
CFRelease(name);
|
||||
if (font == kInvalidFont) return err;
|
||||
|
||||
/* Get a file system reference for the font. */
|
||||
FSRef ref;
|
||||
OSStatus os_err = -1;
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
if (MacOSVersionIsAtLeast(10, 5, 0)) {
|
||||
os_err = ATSFontGetFileReference(font, &ref);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__
|
||||
/* This type was introduced with the 10.5 SDK. */
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
|
||||
#define ATSFSSpec FSSpec
|
||||
#endif
|
||||
FSSpec spec;
|
||||
os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
|
||||
if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (os_err == noErr) {
|
||||
/* Get unix path for file. */
|
||||
UInt8 file_path[PATH_MAX];
|
||||
if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
|
||||
DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
|
||||
err = FT_New_Face(_library, (const char *)file_path, 0, face);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
|
||||
{
|
||||
const char *str;
|
||||
bool result = false;
|
||||
|
||||
callback->FindMissingGlyphs(&str);
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
if (MacOSVersionIsAtLeast(10, 5, 0)) {
|
||||
/* Determine fallback font using CoreText. This uses the language isocode
|
||||
* to find a suitable font. CoreText is available from 10.5 onwards. */
|
||||
char lang[16];
|
||||
if (strcmp(language_isocode, "zh_TW") == 0) {
|
||||
/* Traditional Chinese */
|
||||
strecpy(lang, "zh-Hant", lastof(lang));
|
||||
} else if (strcmp(language_isocode, "zh_CN") == 0) {
|
||||
/* Simplified Chinese */
|
||||
strecpy(lang, "zh-Hans", lastof(lang));
|
||||
} else if (strncmp(language_isocode, "ur", 2) == 0) {
|
||||
/* The urdu alphabet is variant of persian. As OS X has no default
|
||||
* font that advertises an urdu language code, search for persian
|
||||
* support instead. */
|
||||
strecpy(lang, "fa", lastof(lang));
|
||||
} else {
|
||||
/* Just copy the first part of the isocode. */
|
||||
strecpy(lang, language_isocode, lastof(lang));
|
||||
char *sep = strchr(lang, '_');
|
||||
if (sep != NULL) *sep = '\0';
|
||||
}
|
||||
|
||||
CFStringRef lang_code;
|
||||
lang_code = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
|
||||
|
||||
/* Create a font iterator and iterate over all fonts that
|
||||
* are available to the application. */
|
||||
ATSFontIterator itr;
|
||||
ATSFontRef font;
|
||||
ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsUnRestrictedScope, &itr);
|
||||
while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
|
||||
/* Get CoreText font handle. */
|
||||
CTFontRef font_ref = CTFontCreateWithPlatformFont(font, 0.0, NULL, NULL);
|
||||
CFArrayRef langs = CTFontCopySupportedLanguages(font_ref);
|
||||
if (langs != NULL) {
|
||||
/* Font has a list of supported languages. */
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(langs); i++) {
|
||||
CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i);
|
||||
if (CFStringCompare(lang, lang_code, kCFCompareAnchored) == kCFCompareEqualTo) {
|
||||
/* Lang code is supported by font, get full font name. */
|
||||
CFStringRef font_name = CTFontCopyFullName(font_ref);
|
||||
char name[128];
|
||||
CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
|
||||
CFRelease(font_name);
|
||||
/* Skip some inappropriate or ugly looking fonts that have better alternatives. */
|
||||
if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 ||
|
||||
strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 ||
|
||||
strncmp(name, "GB18030 Bitmap", 14) == 0) continue;
|
||||
|
||||
/* Save result. */
|
||||
callback->SetFontNames(settings, name);
|
||||
DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CFRelease(langs);
|
||||
}
|
||||
CFRelease(font_ref);
|
||||
}
|
||||
ATSFontIteratorRelease(&itr);
|
||||
CFRelease(lang_code);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__
|
||||
/* Determine fallback font using ATSUI. This uses a string sample with
|
||||
* missing characters. This is not failure-proof, but a better way like
|
||||
* using the isocode as in the CoreText code path is not available.
|
||||
* ATSUI was deprecated with 10.6 and is only partially available in
|
||||
* 64-bit mode. */
|
||||
|
||||
/* Remove all control characters in the range from SCC_CONTROL_START to
|
||||
* SCC_CONTROL_END as well as all ASCII < 0x20 from the string as it will
|
||||
* mess with the automatic font detection */
|
||||
char buff[256]; // This length is enough to find a suitable replacement font
|
||||
strecpy(buff, str, lastof(buff));
|
||||
str_validate(buff, lastof(buff), SVS_ALLOW_NEWLINE);
|
||||
|
||||
/* Extract a UniChar representation of the sample string. */
|
||||
CFStringRef cf_str = CFStringCreateWithCString(kCFAllocatorDefault, buff, kCFStringEncodingUTF8);
|
||||
if (cf_str == NULL) {
|
||||
/* Something went wrong. Corrupt/invalid sample string? */
|
||||
return false;
|
||||
}
|
||||
CFIndex str_len = CFStringGetLength(cf_str);
|
||||
UniChar string[str_len];
|
||||
CFStringGetCharacters(cf_str, CFRangeMake(0, str_len), string);
|
||||
|
||||
/* Create a default text style with the default font. */
|
||||
ATSUStyle style;
|
||||
ATSUCreateStyle(&style);
|
||||
|
||||
/* Create a text layout object from the sample string using the text style. */
|
||||
UniCharCount run_len = kATSUToTextEnd;
|
||||
ATSUTextLayout text_layout;
|
||||
ATSUCreateTextLayoutWithTextPtr(string, kATSUFromTextBeginning, kATSUToTextEnd, str_len, 1, &run_len, &style, &text_layout);
|
||||
|
||||
/* Try to match a font for the sample text. ATSUMatchFontsToText stops after
|
||||
* it finds the first continuous character run not renderable with the currently
|
||||
* selected font starting at offset. The matching needs to be repeated until
|
||||
* the end of the string is reached to make sure the fallback font matches for
|
||||
* all characters in the string and not only the first run. */
|
||||
UniCharArrayOffset offset = kATSUFromTextBeginning;
|
||||
OSStatus os_err;
|
||||
do {
|
||||
ATSUFontID font;
|
||||
UniCharCount run_len;
|
||||
os_err = ATSUMatchFontsToText(text_layout, offset, kATSUToTextEnd, &font, &offset, &run_len);
|
||||
if (os_err == kATSUFontsMatched) {
|
||||
/* Found a better fallback font. Update the text layout
|
||||
* object with the new font. */
|
||||
ATSUAttributeTag tag = kATSUFontTag;
|
||||
ByteCount size = sizeof(font);
|
||||
ATSUAttributeValuePtr val = &font;
|
||||
ATSUSetAttributes(style, 1, &tag, &size, &val);
|
||||
offset += run_len;
|
||||
}
|
||||
/* Exit if the end of the string is reached or some other error occurred. */
|
||||
} while (os_err == kATSUFontsMatched && offset < (UniCharArrayOffset)str_len);
|
||||
|
||||
if (os_err == noErr || os_err == kATSUFontsMatched) {
|
||||
/* ATSUMatchFontsToText exited normally. Extract font
|
||||
* out of the text layout object. */
|
||||
ATSUFontID font;
|
||||
ByteCount act_len;
|
||||
ATSUGetAttribute(style, kATSUFontTag, sizeof(font), &font, &act_len);
|
||||
|
||||
/* Get unique font name. The result is not a c-string, we have
|
||||
* to leave space for a \0 and terminate it ourselves. */
|
||||
char name[128];
|
||||
ATSUFindFontName(font, kFontUniqueName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, 127, name, &act_len, NULL);
|
||||
name[act_len > 127 ? 127 : act_len] = '\0';
|
||||
|
||||
/* Save Result. */
|
||||
callback->SetFontNames(settings, name);
|
||||
DEBUG(freetype, 2, "ATSUI-Font for %s: %s", language_isocode, name);
|
||||
result = true;
|
||||
}
|
||||
|
||||
ATSUDisposeTextLayout(text_layout);
|
||||
ATSUDisposeStyle(style);
|
||||
CFRelease(cf_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (result && strncmp(settings->medium.font, "Geeza Pro", 9) == 0) {
|
||||
/* The font 'Geeza Pro' is often found for arabic characters, but
|
||||
* it has the 'tiny' problem of not having any latin characters.
|
||||
* 'Arial Unicode MS' on the other hand has arabic and latin glyphs,
|
||||
* but seems to 'forget' to inform the OS about this fact. Manually
|
||||
* substitute the latter for the former if it is loadable. */
|
||||
bool ft_init = _library != NULL;
|
||||
FT_Face face;
|
||||
/* Init FreeType if needed. */
|
||||
if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName("Arial Unicode MS", &face) == FT_Err_Ok) {
|
||||
FT_Done_Face(face);
|
||||
callback->SetFontNames(settings, "Arial Unicode MS");
|
||||
DEBUG(freetype, 1, "Replacing font 'Geeza Pro' with 'Arial Unicode MS'");
|
||||
}
|
||||
if (!ft_init) {
|
||||
/* Uninit FreeType if we did the init. */
|
||||
FT_Done_FreeType(_library);
|
||||
_library = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
callback->FindMissingGlyphs(NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
|
||||
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
/* ========================================================================================
|
||||
* FontConfig (unix) support
|
||||
* ======================================================================================== */
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
|
||||
{
|
||||
FT_Error err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
if (!FcInit()) {
|
||||
ShowInfoF("Unable to load font configuration");
|
||||
} else {
|
||||
FcPattern *match;
|
||||
FcPattern *pat;
|
||||
FcFontSet *fs;
|
||||
FcResult result;
|
||||
char *font_style;
|
||||
char *font_family;
|
||||
|
||||
/* Split & strip the font's style */
|
||||
font_family = strdup(font_name);
|
||||
font_style = strchr(font_family, ',');
|
||||
if (font_style != NULL) {
|
||||
font_style[0] = '\0';
|
||||
font_style++;
|
||||
while (*font_style == ' ' || *font_style == '\t') font_style++;
|
||||
}
|
||||
|
||||
/* Resolve the name and populate the information structure */
|
||||
pat = FcNameParse((FcChar8*)font_family);
|
||||
if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
|
||||
FcConfigSubstitute(0, pat, FcMatchPattern);
|
||||
FcDefaultSubstitute(pat);
|
||||
fs = FcFontSetCreate();
|
||||
match = FcFontMatch(0, pat, &result);
|
||||
|
||||
if (fs != NULL && match != NULL) {
|
||||
int i;
|
||||
FcChar8 *family;
|
||||
FcChar8 *style;
|
||||
FcChar8 *file;
|
||||
FcFontSetAdd(fs, match);
|
||||
|
||||
for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
|
||||
/* Try the new filename */
|
||||
if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
|
||||
FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
|
||||
FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
|
||||
|
||||
/* The correct style? */
|
||||
if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
|
||||
|
||||
/* Font config takes the best shot, which, if the family name is spelled
|
||||
* wrongly a 'random' font, so check whether the family name is the
|
||||
* same as the supplied name */
|
||||
if (strcasecmp(font_family, (char*)family) == 0) {
|
||||
err = FT_New_Face(_library, (char *)file, 0, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(font_family);
|
||||
FcPatternDestroy(pat);
|
||||
FcFontSetDestroy(fs);
|
||||
FcFini();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
|
||||
{
|
||||
if (!FcInit()) return false;
|
||||
|
||||
bool ret = false;
|
||||
|
||||
/* Fontconfig doesn't handle full language isocodes, only the part
|
||||
* before the _ of e.g. en_GB is used, so "remove" everything after
|
||||
* the _. */
|
||||
char lang[16];
|
||||
seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
|
||||
char *split = strchr(lang, '_');
|
||||
if (split != NULL) *split = '\0';
|
||||
|
||||
/* First create a pattern to match the wanted language. */
|
||||
FcPattern *pat = FcNameParse((FcChar8*)lang);
|
||||
/* We only want to know the filename. */
|
||||
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, NULL);
|
||||
/* Get the list of filenames matching the wanted language. */
|
||||
FcFontSet *fs = FcFontList(NULL, pat, os);
|
||||
|
||||
/* We don't need these anymore. */
|
||||
FcObjectSetDestroy(os);
|
||||
FcPatternDestroy(pat);
|
||||
|
||||
if (fs != NULL) {
|
||||
int best_weight = -1;
|
||||
const char *best_font = NULL;
|
||||
|
||||
for (int i = 0; i < fs->nfont; i++) {
|
||||
FcPattern *font = fs->fonts[i];
|
||||
|
||||
FcChar8 *file = NULL;
|
||||
FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
|
||||
if (res != FcResultMatch || file == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get a font with the right spacing .*/
|
||||
int value = 0;
|
||||
FcPatternGetInteger(font, FC_SPACING, 0, &value);
|
||||
if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
|
||||
|
||||
/* Do not use those that explicitly say they're slanted. */
|
||||
FcPatternGetInteger(font, FC_SLANT, 0, &value);
|
||||
if (value != 0) continue;
|
||||
|
||||
/* We want the fatter font as they look better at small sizes. */
|
||||
FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
|
||||
if (value <= best_weight) continue;
|
||||
|
||||
callback->SetFontNames(settings, (const char*)file);
|
||||
|
||||
bool missing = callback->FindMissingGlyphs(NULL);
|
||||
DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
|
||||
|
||||
if (!missing) {
|
||||
best_weight = value;
|
||||
best_font = (const char *)file;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_font != NULL) {
|
||||
ret = true;
|
||||
callback->SetFontNames(settings, best_font);
|
||||
InitFreeType(callback->Monospace());
|
||||
}
|
||||
|
||||
/* Clean up the list of filenames. */
|
||||
FcFontSetDestroy(fs);
|
||||
}
|
||||
|
||||
FcFini();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* without WITH_FONTCONFIG */
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; }
|
||||
#endif /* WITH_FONTCONFIG */
|
||||
|
||||
#endif /* WITH_FREETYPE */
|
|
@ -0,0 +1,44 @@
|
|||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file fontdetection.h Functions related to detecting/finding the right font. */
|
||||
|
||||
#ifndef FONTDETECTION_H
|
||||
#define FONTDETECTION_H
|
||||
|
||||
#include "fontcache.h"
|
||||
|
||||
#ifdef WITH_FREETYPE
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
/**
|
||||
* Load a freetype font face with the given font name.
|
||||
* @param font_name The name of the font to load.
|
||||
* @param face The face that has been found.
|
||||
* @return The error we encountered.
|
||||
*/
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face);
|
||||
|
||||
/**
|
||||
* We would like to have a fallback font as the current one
|
||||
* doesn't contain all characters we need.
|
||||
* This function must set all fonts of settings.
|
||||
* @param settings the settings to overwrite the fontname of.
|
||||
* @param language_isocode the language, e.g. en_GB.
|
||||
* @param winlangid the language ID windows style.
|
||||
* @param callback The function to call to check for missing glyphs.
|
||||
* @return true if a font has been set, false otherwise.
|
||||
*/
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, class MissingGlyphSearcher *callback);
|
||||
|
||||
#endif /* WITH_FREETYPE */
|
||||
|
||||
#endif
|
681
src/gfx.cpp
681
src/gfx.cpp
|
@ -10,8 +10,7 @@
|
|||
/** @file gfx.cpp Handling of drawing text and other gfx related stuff. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "gfx_func.h"
|
||||
#include "fontcache.h"
|
||||
#include "gfx_layout.h"
|
||||
#include "progress.h"
|
||||
#include "zoom_func.h"
|
||||
#include "blitter/factory.hpp"
|
||||
|
@ -45,9 +44,6 @@ SwitchMode _switch_mode; ///< The next mainloop command.
|
|||
PauseModeByte _pause_mode;
|
||||
Palette _cur_palette;
|
||||
|
||||
static Dimension _max_char_size[FS_END]; ///< Cache of the maximum size of any character of a font.
|
||||
static int _max_char_height; ///< Cache of the height of the largest font
|
||||
static int _max_char_width; ///< Cache of the width of the largest font
|
||||
static byte _stringwidth_table[FS_END][224]; ///< Cache containing width of often used characters. @see GetCharacterWidth()
|
||||
DrawPixelInfo *_cur_dpi;
|
||||
byte _colour_gradient[COLOUR_END][8];
|
||||
|
@ -296,198 +292,74 @@ static void SetColourRemap(TextColour colour)
|
|||
_colour_remap_ptr = _string_colourremap;
|
||||
}
|
||||
|
||||
#if !defined(WITH_ICU)
|
||||
static WChar *HandleBiDiAndArabicShapes(WChar *text) { return text; }
|
||||
#else
|
||||
#include <unicode/ubidi.h>
|
||||
#include <unicode/ushape.h>
|
||||
#include <unicode/ustring.h>
|
||||
|
||||
/**
|
||||
* Function to be able to handle right-to-left text and Arabic chars properly.
|
||||
*
|
||||
* First: right-to-left (RTL) is stored 'logically' in almost all applications
|
||||
* and so do we. This means that their text is stored from right to the
|
||||
* left in memory and any non-RTL text (like numbers or English) are
|
||||
* then stored from left-to-right. When we want to actually draw the
|
||||
* text we need to reverse the RTL text in memory, which is what
|
||||
* happens in ubidi_writeReordered.
|
||||
* Second: Arabic characters "differ" based on their context. To draw the
|
||||
* correct variant we pass it through u_shapeArabic. This function can
|
||||
* add or remove some characters. This is the reason for the lastof
|
||||
* so we know till where we can fill the output.
|
||||
*
|
||||
* Sadly enough these functions work with a custom character format, UChar,
|
||||
* which isn't the same size as WChar. Because of that we need to transform
|
||||
* our text first to UChars and then back to something we can use.
|
||||
*
|
||||
* To be able to truncate strings properly you must truncate before passing to
|
||||
* this function. This way the logical begin of the string remains and the end
|
||||
* gets chopped of instead of the other way around.
|
||||
*
|
||||
* The reshaping of Arabic characters might increase or decrease the width of
|
||||
* the characters/string. So it might still overflow after truncation, though
|
||||
* the chance is fairly slim as most characters get shorter instead of longer.
|
||||
* @param buffer the buffer to read from/to
|
||||
* @param lastof the end of the buffer
|
||||
* @return the buffer to draw from
|
||||
*/
|
||||
static WChar *HandleBiDiAndArabicShapes(WChar *buffer)
|
||||
{
|
||||
UChar input[DRAW_STRING_BUFFER];
|
||||
UChar intermediate[DRAW_STRING_BUFFER];
|
||||
static WChar output[DRAW_STRING_BUFFER];
|
||||
|
||||
/* Transform from UTF-32 to internal ICU format of UTF-16. */
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
int32_t length = 0;
|
||||
u_strFromUTF32(input, lengthof(input), &length, (UChar32 *)buffer, -1, &err);
|
||||
if (U_FAILURE(err)) return buffer;
|
||||
|
||||
UBiDi *para = ubidi_openSized(length, 0, &err);
|
||||
if (para == NULL) return buffer;
|
||||
|
||||
ubidi_setPara(para, input, length, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, &err);
|
||||
length = ubidi_writeReordered(para, intermediate, lengthof(intermediate), UBIDI_REMOVE_BIDI_CONTROLS, &err);
|
||||
length = u_shapeArabic(intermediate, length, input, lengthof(input), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
|
||||
ubidi_close(para);
|
||||
if (U_FAILURE(err)) return buffer;
|
||||
|
||||
/* Transform back to UTF-32. */
|
||||
u_strToUTF32((UChar32 *)output, lengthof(output), NULL, input, length, &err);
|
||||
if (U_FAILURE(err)) return buffer;
|
||||
|
||||
/* u_strToUTF32 doesn't add a NUL charcter if the buffer is too small, be safe. */
|
||||
output[lengthof(output) - 1] = '\0';
|
||||
return output;
|
||||
}
|
||||
#endif /* WITH_ICU */
|
||||
|
||||
|
||||
/**
|
||||
* Truncate a given string to a maximum width if necessary.
|
||||
* If the string is truncated, add three dots ('...') to show this.
|
||||
* @param *str string that is checked and possibly truncated
|
||||
* @param maxw maximum width in pixels of the string
|
||||
* @param start_fontsize Fontsize to start the text with
|
||||
* @return new width of (truncated) string
|
||||
*/
|
||||
static int TruncateString(char *str, int maxw, FontSize start_fontsize)
|
||||
{
|
||||
int w = 0;
|
||||
FontSize size = start_fontsize;
|
||||
int ddd, ddd_w;
|
||||
|
||||
WChar c;
|
||||
char *ddd_pos;
|
||||
|
||||
ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
|
||||
|
||||
for (ddd_pos = str; (c = Utf8Consume(const_cast<const char **>(&str))) != '\0'; ) {
|
||||
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
|
||||
w += GetCharacterWidth(size, c);
|
||||
|
||||
if (w > maxw) {
|
||||
/* string got too big... insert dotdotdot, but make sure we do not
|
||||
* print anything beyond the string termination character. */
|
||||
for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
|
||||
*ddd_pos = '\0';
|
||||
return ddd_w;
|
||||
}
|
||||
} else {
|
||||
if (c == SCC_TINYFONT) {
|
||||
size = FS_SMALL;
|
||||
ddd = GetCharacterWidth(size, '.') * 3;
|
||||
} else if (c == SCC_BIGFONT) {
|
||||
size = FS_LARGE;
|
||||
ddd = GetCharacterWidth(size, '.') * 3;
|
||||
} else if (c == '\n') {
|
||||
DEBUG(misc, 0, "Drawing string using newlines with DrawString instead of DrawStringMultiLine. Please notify the developers of this: [%s]", str);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember the last position where three dots fit. */
|
||||
if (w + ddd < maxw) {
|
||||
ddd_w = w + ddd;
|
||||
ddd_pos = str;
|
||||
}
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static int ReallyDoDrawString(const WChar *string, int x, int y, DrawStringParams ¶ms, bool parse_string_also_when_clipped = false);
|
||||
|
||||
/**
|
||||
* Get the real width of the string.
|
||||
* @param str the string to draw
|
||||
* @param start_fontsize Fontsize to start the text with
|
||||
* @return the width.
|
||||
*/
|
||||
static int GetStringWidth(const WChar *str, FontSize start_fontsize)
|
||||
{
|
||||
FontSize size = start_fontsize;
|
||||
int max_width;
|
||||
int width;
|
||||
WChar c;
|
||||
|
||||
width = max_width = 0;
|
||||
for (;;) {
|
||||
c = *str++;
|
||||
if (c == 0) break;
|
||||
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
|
||||
width += GetCharacterWidth(size, c);
|
||||
} else {
|
||||
switch (c) {
|
||||
case SCC_TINYFONT: size = FS_SMALL; break;
|
||||
case SCC_BIGFONT: size = FS_LARGE; break;
|
||||
case '\n':
|
||||
max_width = max(max_width, width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max(max_width, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw string, possibly truncated to make it fit in its allocated space
|
||||
*
|
||||
* @param left The left most position to draw on.
|
||||
* @param right The right most position to draw on.
|
||||
* @param top The top most position to draw on.
|
||||
* @param str String to draw.
|
||||
* @param last The end of the string buffer to draw.
|
||||
* @param params Text drawing parameters.
|
||||
* @param align The alignment of the string when drawing left-to-right. In the
|
||||
* case a right-to-left language is chosen this is inverted so it
|
||||
* will be drawn in the right direction.
|
||||
* Drawing routine for drawing a laid out line of text.
|
||||
* @param line String to draw.
|
||||
* @param y The top most position to draw on.
|
||||
* @param left The left most position to draw on.
|
||||
* @param right The right most position to draw on.
|
||||
* @param align The alignment of the string when drawing left-to-right. In the
|
||||
* case a right-to-left language is chosen this is inverted so it
|
||||
* will be drawn in the right direction.
|
||||
* @param underline Whether to underline what has been drawn or not.
|
||||
* @param truncate Whether to truncate the string or not.
|
||||
*
|
||||
* @return In case of left or center alignment the right most pixel we have drawn to.
|
||||
* In case of right alignment the left most pixel we have drawn to.
|
||||
*/
|
||||
static int DrawString(int left, int right, int top, char *str, const char *last, DrawStringParams ¶ms, StringAlignment align, bool underline = false, bool truncate = true)
|
||||
static int DrawLayoutLine(ParagraphLayout::Line *line, int y, int left, int right, StringAlignment align, bool underline)
|
||||
{
|
||||
if (truncate) TruncateString(str, right - left + 1, params.fontsize);
|
||||
if (line->countRuns() == 0) return 0;
|
||||
|
||||
WChar draw_buffer[DRAW_STRING_BUFFER];
|
||||
WChar *p = draw_buffer;
|
||||
int w = line->getWidth();
|
||||
int h = line->getLeading();
|
||||
|
||||
const char *text = str;
|
||||
for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
|
||||
*p++ = c;
|
||||
/*
|
||||
* The following is needed for truncation.
|
||||
* Depending on the text direction, we either remove bits at the rear
|
||||
* or the front. For this we shift the entire area to draw so it fits
|
||||
* within the left/right bounds and the side we do not truncate it on.
|
||||
* Then we determine the truncation location, i.e. glyphs that fall
|
||||
* outside of the range min_x - max_x will not be drawn; they are thus
|
||||
* the truncated glyphs.
|
||||
*
|
||||
* At a later step we insert the dots.
|
||||
*/
|
||||
|
||||
int max_w = right - left + 1; // The maximum width.
|
||||
|
||||
int offset_x = 0; // The offset we need for positioning the glyphs
|
||||
int min_x = left; // The minimum x position to draw normal glyphs on.
|
||||
int max_x = right; // The maximum x position to draw normal glyphs on.
|
||||
|
||||
bool truncation = max_w < w; // Whether we need to do truncation.
|
||||
int dot_width = 0; // Cache for the width of the dot.
|
||||
const Sprite *dot_sprite = NULL; // Cache for the sprite of the dot.
|
||||
|
||||
if (truncation) {
|
||||
/*
|
||||
* Assumption may be made that all fonts of a run are of the same size.
|
||||
* In any case, we'll use these dots for the abbreviation, so even if
|
||||
* another size would be chosen it won't have truncated too little for
|
||||
* the truncation dots.
|
||||
*/
|
||||
FontCache *fc = ((const Font*)line->getVisualRun(0)->getFont())->fc;
|
||||
GlyphID dot_glyph = fc->MapCharToGlyph('.');
|
||||
dot_width = fc->GetGlyphWidth(dot_glyph);
|
||||
dot_sprite = fc->GetGlyph(dot_glyph);
|
||||
|
||||
if (_current_text_dir == TD_RTL) {
|
||||
min_x += 3 * dot_width;
|
||||
offset_x = w - 3 * dot_width - max_w;
|
||||
} else {
|
||||
max_x -= 3 * dot_width;
|
||||
}
|
||||
|
||||
w = max_w;
|
||||
}
|
||||
*p++ = '\0';
|
||||
|
||||
/* In case we have a RTL language we swap the alignment. */
|
||||
if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
|
||||
|
||||
WChar *to_draw = HandleBiDiAndArabicShapes(draw_buffer);
|
||||
int w = GetStringWidth(to_draw, params.fontsize);
|
||||
|
||||
/* right is the right most position to draw on. In this case we want to do
|
||||
* calculations with the width of the string. In comparison right can be
|
||||
* seen as lastof(todraw) and width as lengthof(todraw). They differ by 1.
|
||||
|
@ -513,9 +385,55 @@ static int DrawString(int left, int right, int top, char *str, const char *last,
|
|||
NOT_REACHED();
|
||||
}
|
||||
|
||||
ReallyDoDrawString(to_draw, left, top, params, !truncate);
|
||||
for (int run_index = 0; run_index < line->countRuns(); run_index++) {
|
||||
const ParagraphLayout::VisualRun *run = line->getVisualRun(run_index);
|
||||
const Font *f = (const Font*)run->getFont();
|
||||
|
||||
FontCache *fc = f->fc;
|
||||
TextColour colour = f->colour;
|
||||
SetColourRemap(colour);
|
||||
|
||||
DrawPixelInfo *dpi = _cur_dpi;
|
||||
int dpi_left = dpi->left;
|
||||
int dpi_right = dpi->left + dpi->width - 1;
|
||||
|
||||
bool draw_shadow = fc->GetDrawGlyphShadow() && colour != TC_BLACK;
|
||||
|
||||
for (int i = 0; i < run->getGlyphCount(); i++) {
|
||||
GlyphID glyph = run->getGlyphs()[i];
|
||||
|
||||
/* Not a valid glyph (empty) */
|
||||
if (glyph == 0xFFFF) continue;
|
||||
|
||||
int begin_x = run->getPositions()[i * 2] + left - offset_x;
|
||||
int end_x = run->getPositions()[i * 2 + 2] + left - offset_x - 1;
|
||||
int top = run->getPositions()[i * 2 + 1] + y;
|
||||
|
||||
/* Truncated away. */
|
||||
if (truncation && (begin_x < min_x || end_x > max_x)) continue;
|
||||
|
||||
const Sprite *sprite = fc->GetGlyph(glyph);
|
||||
/* Check clipping (the "+ 1" is for the shadow). */
|
||||
if (begin_x + sprite->x_offs > dpi_right || begin_x + sprite->x_offs + sprite->width /* - 1 + 1 */ < dpi_left) continue;
|
||||
|
||||
if (draw_shadow && (glyph & SPRITE_GLYPH) == 0) {
|
||||
SetColourRemap(TC_BLACK);
|
||||
GfxMainBlitter(sprite, begin_x + 1, top + 1, BM_COLOUR_REMAP);
|
||||
SetColourRemap(colour);
|
||||
}
|
||||
GfxMainBlitter(sprite, begin_x, top, BM_COLOUR_REMAP);
|
||||
}
|
||||
}
|
||||
|
||||
if (truncation) {
|
||||
int x = (_current_text_dir == TD_RTL) ? left : (right - 3 * dot_width);
|
||||
for (int i = 0; i < 3; i++, x += dot_width) {
|
||||
GfxMainBlitter(dot_sprite, x, y, BM_COLOUR_REMAP);
|
||||
}
|
||||
}
|
||||
|
||||
if (underline) {
|
||||
GfxFillRect(left, top + FONT_HEIGHT_NORMAL, right, top + FONT_HEIGHT_NORMAL, _string_colourremap[1]);
|
||||
GfxFillRect(left, y + h, right, y + h, _string_colourremap[1]);
|
||||
}
|
||||
|
||||
return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
|
||||
|
@ -537,10 +455,10 @@ static int DrawString(int left, int right, int top, char *str, const char *last,
|
|||
*/
|
||||
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
|
||||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
strecpy(buffer, str, lastof(buffer));
|
||||
DrawStringParams params(colour, fontsize);
|
||||
return DrawString(left, right, top, buffer, lastof(buffer), params, align, underline);
|
||||
Layouter layout(str, INT32_MAX, colour, fontsize);
|
||||
if (layout.Length() == 0) return 0;
|
||||
|
||||
return DrawLayoutLine(*layout.Begin(), top, left, right, align, underline);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,145 +479,21 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour, St
|
|||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
GetString(buffer, str, lastof(buffer));
|
||||
DrawStringParams params(colour, fontsize);
|
||||
return DrawString(left, right, top, buffer, lastof(buffer), params, align, underline);
|
||||
return DrawString(left, right, top, buffer, colour, align, underline, fontsize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Correct' a string to a maximum length. Longer strings will be cut into
|
||||
* additional lines at whitespace characters if possible. The string parameter
|
||||
* is modified with terminating characters mid-string which are the
|
||||
* placeholders for the newlines.
|
||||
* The string WILL be truncated if there was no whitespace for the current
|
||||
* line's maximum width.
|
||||
*
|
||||
* @note To know if the terminating '\0' is the string end or just a
|
||||
* newline, the returned 'num' value should be consulted. The num'th '\0',
|
||||
* starting with index 0 is the real string end.
|
||||
*
|
||||
* @param str string to check and correct for length restrictions
|
||||
* @param last the last valid location (for '\0') in the buffer of str
|
||||
* @param maxw the maximum width the string can have on one line
|
||||
* @param size Fontsize to start the text with
|
||||
* @return return a 32bit wide number consisting of 2 packed values:
|
||||
* 0 - 15 the number of lines ADDED to the string
|
||||
* 16 - 31 the fontsize in which the length calculation was done at
|
||||
*/
|
||||
uint32 FormatStringLinebreaks(char *str, const char *last, int maxw, FontSize size)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
assert(maxw > 0);
|
||||
|
||||
for (;;) {
|
||||
/* The character *after* the last space. */
|
||||
char *last_space = NULL;
|
||||
int w = 0;
|
||||
|
||||
for (;;) {
|
||||
WChar c = Utf8Consume(const_cast<const char **>(&str));
|
||||
/* whitespace is where we will insert the line-break */
|
||||
if (IsWhitespace(c)) last_space = str;
|
||||
|
||||
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
|
||||
int char_w = GetCharacterWidth(size, c);
|
||||
w += char_w;
|
||||
if (w > maxw) {
|
||||
/* The string is longer than maximum width so we need to decide
|
||||
* what to do with it. */
|
||||
if (w == char_w) {
|
||||
/* The character is wider than allowed width; don't know
|
||||
* what to do with this case... bail out! */
|
||||
return num + (size << 16);
|
||||
}
|
||||
if (last_space == NULL) {
|
||||
/* No space has been found. Just terminate at our current
|
||||
* location. This usually happens for languages that do not
|
||||
* require spaces in strings, like Chinese, Japanese and
|
||||
* Korean. For other languages terminating mid-word might
|
||||
* not be the best, but terminating the whole string instead
|
||||
* of continuing the word at the next line is worse. */
|
||||
str = Utf8PrevChar(str);
|
||||
size_t len = strlen(str);
|
||||
char *terminator = str + len;
|
||||
|
||||
/* The string location + length of the string + 1 for '\0'
|
||||
* always fits; otherwise there's no trailing '\0' and it
|
||||
* it not a valid string. */
|
||||
assert(terminator <= last);
|
||||
assert(*terminator == '\0');
|
||||
|
||||
/* If the string is too long we have to terminate it earlier. */
|
||||
if (terminator == last) {
|
||||
/* Get the 'begin' of the previous character and make that
|
||||
* the terminator of the string; we truncate it 'early'. */
|
||||
*Utf8PrevChar(terminator) = '\0';
|
||||
len = strlen(str);
|
||||
}
|
||||
/* Also move the terminator! */
|
||||
memmove(str + 1, str, len + 1);
|
||||
*str = '\0';
|
||||
/* str needs to point to the character *after* the last space */
|
||||
str++;
|
||||
} else {
|
||||
/* A space is found; perfect place to terminate */
|
||||
str = last_space;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (c) {
|
||||
case '\0': return num + (size << 16);
|
||||
case SCC_TINYFONT: size = FS_SMALL; break;
|
||||
case SCC_BIGFONT: size = FS_LARGE; break;
|
||||
case '\n': goto end_of_inner_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
end_of_inner_loop:
|
||||
/* String didn't fit on line (or a '\n' was encountered), so 'dummy' terminate
|
||||
* and increase linecount. We use Utf8PrevChar() as also non 1 char long
|
||||
* whitespace separators are supported */
|
||||
num++;
|
||||
char *s = Utf8PrevChar(str);
|
||||
*s++ = '\0';
|
||||
|
||||
/* In which case (see above) we will shift remainder to left and close the gap */
|
||||
if (str - s >= 1) {
|
||||
for (; str[-1] != '\0';) *s++ = *str++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates height of string (in pixels). Accepts multiline string with '\0' as separators.
|
||||
* @param src string to check
|
||||
* @param num number of extra lines (output of FormatStringLinebreaks())
|
||||
* @param start_fontsize Fontsize to start the text with
|
||||
* @note assumes text won't be truncated. FormatStringLinebreaks() is a good way to ensure that.
|
||||
* Calculates height of string (in pixels). The string is changed to a multiline string if needed.
|
||||
* @param str string to check
|
||||
* @param maxw maximum string width
|
||||
* @return height of pixels of string when it is drawn
|
||||
*/
|
||||
static int GetMultilineStringHeight(const char *src, int num, FontSize start_fontsize)
|
||||
static int GetStringHeight(const char *str, int maxw)
|
||||
{
|
||||
int maxy = 0;
|
||||
int y = 0;
|
||||
int fh = GetCharacterHeight(start_fontsize);
|
||||
|
||||
for (;;) {
|
||||
WChar c = Utf8Consume(&src);
|
||||
|
||||
switch (c) {
|
||||
case 0: y += fh; if (--num < 0) return maxy; break;
|
||||
case '\n': y += fh; break;
|
||||
case SCC_TINYFONT: fh = GetCharacterHeight(FS_SMALL); break;
|
||||
case SCC_BIGFONT: fh = GetCharacterHeight(FS_LARGE); break;
|
||||
default: maxy = max<int>(maxy, y + fh); break;
|
||||
}
|
||||
}
|
||||
Layouter layout(str, maxw);
|
||||
return layout.GetBounds().height;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates height of string (in pixels). The string is changed to a multiline string if needed.
|
||||
* @param str string to check
|
||||
|
@ -709,12 +503,23 @@ static int GetMultilineStringHeight(const char *src, int num, FontSize start_fon
|
|||
int GetStringHeight(StringID str, int maxw)
|
||||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
GetString(buffer, str, lastof(buffer));
|
||||
return GetStringHeight(buffer, maxw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates number of lines of string. The string is changed to a multiline string if needed.
|
||||
* @param str string to check
|
||||
* @param maxw maximum string width
|
||||
* @return number of lines of string when it is drawn
|
||||
*/
|
||||
int GetStringLineCount(StringID str, int maxw)
|
||||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
GetString(buffer, str, lastof(buffer));
|
||||
|
||||
uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw);
|
||||
|
||||
return GetMultilineStringHeight(buffer, GB(tmp, 0, 16), FS_NORMAL);
|
||||
Layouter layout(buffer, maxw);
|
||||
return layout.Length();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -729,24 +534,6 @@ Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestio
|
|||
return box;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates height of string (in pixels). The string is changed to a multiline string if needed.
|
||||
* @param str string to check
|
||||
* @param maxw maximum string width
|
||||
* @return height of pixels of string when it is drawn
|
||||
*/
|
||||
int GetStringHeight(const char *str, int maxw)
|
||||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
|
||||
strecpy(buffer, str, lastof(buffer));
|
||||
|
||||
uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw);
|
||||
|
||||
return GetMultilineStringHeight(buffer, GB(tmp, 0, 16), FS_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate string bounding box for multi-line strings.
|
||||
* @param str String to check.
|
||||
|
@ -767,7 +554,6 @@ Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &sugges
|
|||
* @param top The top most position to draw on.
|
||||
* @param bottom The bottom most position to draw on.
|
||||
* @param str String to draw.
|
||||
* @param last The end of the string buffer to draw.
|
||||
* @param colour Colour used for drawing the string, see DoDrawString() for details
|
||||
* @param align The horizontal and vertical alignment of the string.
|
||||
* @param underline Whether to underline all strings
|
||||
|
@ -775,7 +561,7 @@ Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &sugges
|
|||
*
|
||||
* @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written.
|
||||
*/
|
||||
static int DrawStringMultiLine(int left, int right, int top, int bottom, char *str, const char *last, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
|
||||
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
|
||||
{
|
||||
int maxw = right - left + 1;
|
||||
int maxh = bottom - top + 1;
|
||||
|
@ -784,25 +570,8 @@ static int DrawStringMultiLine(int left, int right, int top, int bottom, char *s
|
|||
* do we really want to support fonts of 0 or less pixels high? */
|
||||
if (maxh <= 0) return top;
|
||||
|
||||
uint32 tmp = FormatStringLinebreaks(str, last, maxw);
|
||||
int num = GB(tmp, 0, 16) + 1;
|
||||
|
||||
int mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
|
||||
int total_height = num * mt;
|
||||
|
||||
int skip_lines = 0;
|
||||
if (total_height > maxh) {
|
||||
if (maxh < mt) return top; // Not enough room for a single line.
|
||||
if ((align & SA_VERT_MASK) == SA_BOTTOM) {
|
||||
skip_lines = num;
|
||||
num = maxh / mt;
|
||||
skip_lines -= num;
|
||||
} else {
|
||||
num = maxh / mt;
|
||||
}
|
||||
total_height = num * mt;
|
||||
}
|
||||
|
||||
Layouter layout(str, maxw, colour, fontsize);
|
||||
int total_height = layout.GetBounds().height;
|
||||
int y;
|
||||
switch (align & SA_VERT_MASK) {
|
||||
case SA_TOP:
|
||||
|
@ -820,62 +589,23 @@ static int DrawStringMultiLine(int left, int right, int top, int bottom, char *s
|
|||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
const char *src = str;
|
||||
DrawStringParams params(colour, fontsize);
|
||||
int written_top = bottom; // Uppermost position of rendering a line of text
|
||||
for (;;) {
|
||||
if (skip_lines == 0) {
|
||||
char buf2[DRAW_STRING_BUFFER];
|
||||
strecpy(buf2, src, lastof(buf2));
|
||||
DrawString(left, right, y, buf2, lastof(buf2), params, align, underline, false);
|
||||
if (written_top > y) written_top = y;
|
||||
y += mt;
|
||||
num--;
|
||||
}
|
||||
int last_line = top;
|
||||
int first_line = bottom;
|
||||
|
||||
for (;;) {
|
||||
WChar c = Utf8Consume(&src);
|
||||
if (c == 0) {
|
||||
break;
|
||||
} else if (skip_lines > 0) {
|
||||
/* Skipped drawing, so do additional processing to update params. */
|
||||
if (c >= SCC_BLUE && c <= SCC_BLACK) {
|
||||
params.SetColour((TextColour)(c - SCC_BLUE));
|
||||
} else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour.
|
||||
params.SetPreviousColour();
|
||||
} else if (c == SCC_TINYFONT) {
|
||||
params.SetFontSize(FS_SMALL);
|
||||
} else if (c == SCC_BIGFONT) {
|
||||
params.SetFontSize(FS_LARGE);
|
||||
}
|
||||
for (ParagraphLayout::Line **iter = layout.Begin(); iter != layout.End(); iter++) {
|
||||
ParagraphLayout::Line *line = *iter;
|
||||
|
||||
}
|
||||
int line_height = line->getLeading();
|
||||
if (y >= top && y < bottom) {
|
||||
last_line = y + line_height;
|
||||
if (first_line > y) first_line = y;
|
||||
|
||||
DrawLayoutLine(line, y, left, right, align, underline);
|
||||
}
|
||||
if (skip_lines > 0) skip_lines--;
|
||||
if (num == 0) return ((align & SA_VERT_MASK) == SA_BOTTOM) ? written_top : y;
|
||||
y += line_height;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw string, possibly over multiple lines.
|
||||
*
|
||||
* @param left The left most position to draw on.
|
||||
* @param right The right most position to draw on.
|
||||
* @param top The top most position to draw on.
|
||||
* @param bottom The bottom most position to draw on.
|
||||
* @param str String to draw.
|
||||
* @param colour Colour used for drawing the string, see DoDrawString() for details
|
||||
* @param align The horizontal and vertical alignment of the string.
|
||||
* @param underline Whether to underline all strings
|
||||
* @param fontsize The size of the initial characters.
|
||||
*
|
||||
* @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written.
|
||||
*/
|
||||
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
|
||||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
strecpy(buffer, str, lastof(buffer));
|
||||
return DrawStringMultiLine(left, right, top, bottom, buffer, lastof(buffer), colour, align, underline, fontsize);
|
||||
return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -897,7 +627,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str,
|
|||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
GetString(buffer, str, lastof(buffer));
|
||||
return DrawStringMultiLine(left, right, top, bottom, buffer, lastof(buffer), colour, align, underline, fontsize);
|
||||
return DrawStringMultiLine(left, right, top, bottom, buffer, colour, align, underline, fontsize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -912,33 +642,8 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str,
|
|||
*/
|
||||
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
|
||||
{
|
||||
FontSize size = start_fontsize;
|
||||
Dimension br;
|
||||
uint max_width;
|
||||
WChar c;
|
||||
|
||||
br.width = br.height = max_width = 0;
|
||||
for (;;) {
|
||||
c = Utf8Consume(&str);
|
||||
if (c == 0) break;
|
||||
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
|
||||
br.width += GetCharacterWidth(size, c);
|
||||
} else {
|
||||
switch (c) {
|
||||
case SCC_TINYFONT: size = FS_SMALL; break;
|
||||
case SCC_BIGFONT: size = FS_LARGE; break;
|
||||
case '\n':
|
||||
br.height += GetCharacterHeight(size);
|
||||
if (br.width > max_width) max_width = br.width;
|
||||
br.width = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
br.height += GetCharacterHeight(size);
|
||||
|
||||
br.width = max(br.width, max_width);
|
||||
return br;
|
||||
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
|
||||
return layout.GetBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -968,86 +673,6 @@ void DrawCharCentered(WChar c, int x, int y, TextColour colour)
|
|||
GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a string at the given coordinates with the given colour.
|
||||
* While drawing the string, parse it in case some formatting is specified,
|
||||
* like new colour, new size or even positioning.
|
||||
* @param string The string to draw. This is already bidi reordered.
|
||||
* @param x Offset from left side of the screen
|
||||
* @param y Offset from top side of the screen
|
||||
* @param params Text drawing parameters
|
||||
* @param parse_string_also_when_clipped
|
||||
* By default, always test the available space where to draw the string.
|
||||
* When in multiline drawing, it would already be done,
|
||||
* so no need to re-perform the same kind (more or less) of verifications.
|
||||
* It's not only an optimisation, it's also a way to ensures the string will be parsed
|
||||
* (as there are certain side effects on global variables, which are important for the next line)
|
||||
* @return the x-coordinates where the drawing has finished.
|
||||
* If nothing is drawn, the originally passed x-coordinate is returned
|
||||
*/
|
||||
static int ReallyDoDrawString(const WChar *string, int x, int y, DrawStringParams ¶ms, bool parse_string_also_when_clipped)
|
||||
{
|
||||
DrawPixelInfo *dpi = _cur_dpi;
|
||||
bool draw_shadow = GetDrawGlyphShadow();
|
||||
WChar c;
|
||||
int xo = x;
|
||||
|
||||
if (!parse_string_also_when_clipped) {
|
||||
/* in "mode multiline", the available space have been verified. Not in regular one.
|
||||
* So if the string cannot be drawn, return the original start to say so.*/
|
||||
if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
|
||||
}
|
||||
|
||||
switch_colour:;
|
||||
SetColourRemap(params.cur_colour);
|
||||
|
||||
check_bounds:
|
||||
if (y + _max_char_height <= dpi->top || dpi->top + dpi->height <= y) {
|
||||
skip_char:;
|
||||
for (;;) {
|
||||
c = *string++;
|
||||
if (!IsPrintable(c)) goto skip_cont;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
c = *string++;
|
||||
skip_cont:;
|
||||
if (c == 0) {
|
||||
return x; // Nothing more to draw, get out. And here is the new x position
|
||||
}
|
||||
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
|
||||
if (x >= dpi->left + dpi->width) goto skip_char;
|
||||
if (x + _max_char_width >= dpi->left) {
|
||||
const Sprite *glyph = GetGlyph(params.fontsize, c);
|
||||
if (draw_shadow && params.fontsize == FS_NORMAL && params.cur_colour != TC_BLACK && !(c >= SCC_SPRITE_START && c <= SCC_SPRITE_END)) {
|
||||
SetColourRemap(TC_BLACK);
|
||||
GfxMainBlitter(glyph, x + 1, y + 1, BM_COLOUR_REMAP);
|
||||
SetColourRemap(params.cur_colour);
|
||||
}
|
||||
GfxMainBlitter(glyph, x, y, BM_COLOUR_REMAP);
|
||||
}
|
||||
x += GetCharacterWidth(params.fontsize, c);
|
||||
} else if (c == '\n') { // newline = {}
|
||||
x = xo; // We require a new line, so the x coordinate is reset
|
||||
y += GetCharacterHeight(params.fontsize);
|
||||
goto check_bounds;
|
||||
} else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change colour?
|
||||
params.SetColour((TextColour)(c - SCC_BLUE));
|
||||
goto switch_colour;
|
||||
} else if (c == SCC_PREVIOUS_COLOUR) { // revert to the previous colour
|
||||
params.SetPreviousColour();
|
||||
goto switch_colour;
|
||||
} else if (c == SCC_TINYFONT) { // {TINYFONT}
|
||||
params.SetFontSize(FS_SMALL);
|
||||
} else if (c == SCC_BIGFONT) { // {BIGFONT}
|
||||
params.SetFontSize(FS_LARGE);
|
||||
} else if (!IsTextDirectionChar(c)) {
|
||||
DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a sprite.
|
||||
* @param sprid Sprite to examine.
|
||||
|
@ -1462,23 +1087,9 @@ TextColour GetContrastColour(uint8 background)
|
|||
void LoadStringWidthTable(bool monospace)
|
||||
{
|
||||
for (FontSize fs = monospace ? FS_MONO : FS_BEGIN; fs < (monospace ? FS_END : FS_MONO); fs++) {
|
||||
_max_char_size[fs].width = 0;
|
||||
_max_char_size[fs].height = GetCharacterHeight(fs);
|
||||
for (uint i = 0; i != 224; i++) {
|
||||
_stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
|
||||
_max_char_size[fs].width = max<int>(_max_char_size[fs].width, _stringwidth_table[fs][i]);
|
||||
}
|
||||
|
||||
/* Needed because they need to be 1 more than the widest. */
|
||||
_max_char_size[fs].width++;
|
||||
_max_char_size[fs].height++;
|
||||
}
|
||||
|
||||
_max_char_width = 0;
|
||||
_max_char_height = 0;
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
_max_char_width = max<int>(_max_char_width, _max_char_size[fs].width);
|
||||
_max_char_height = max<int>(_max_char_height, _max_char_size[fs].height);
|
||||
}
|
||||
|
||||
ReInitAllWindows();
|
||||
|
|
|
@ -121,8 +121,8 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
|
|||
|
||||
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize = FS_NORMAL);
|
||||
Dimension GetStringBoundingBox(StringID strid);
|
||||
uint32 FormatStringLinebreaks(char *str, const char *last, int maxw, FontSize start_fontsize = FS_NORMAL);
|
||||
int GetStringHeight(StringID str, int maxw);
|
||||
int GetStringLineCount(StringID str, int maxw);
|
||||
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
|
||||
Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &suggestion);
|
||||
void LoadStringWidthTable(bool monospace = false);
|
||||
|
@ -151,17 +151,7 @@ byte GetCharacterWidth(FontSize size, uint32 key);
|
|||
byte GetDigitWidth(FontSize size = FS_NORMAL);
|
||||
void GetBroadestDigit(uint *front, uint *next, FontSize size = FS_NORMAL);
|
||||
|
||||
/**
|
||||
* Get height of a character for a given font size.
|
||||
* @param size Font size to get height of
|
||||
* @return Height of characters in the given font (pixels)
|
||||
*/
|
||||
static inline byte GetCharacterHeight(FontSize size)
|
||||
{
|
||||
assert(size < FS_END);
|
||||
extern int _font_height[FS_END];
|
||||
return _font_height[size];
|
||||
}
|
||||
int GetCharacterHeight(FontSize size);
|
||||
|
||||
/** Height of characters in the small (#FS_SMALL) font. */
|
||||
#define FONT_HEIGHT_SMALL (GetCharacterHeight(FS_SMALL))
|
||||
|
|
|
@ -0,0 +1,489 @@
|
|||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file gfx_layout.cpp Handling of laying out text. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "gfx_layout.h"
|
||||
#include "string_func.h"
|
||||
#include "strings_func.h"
|
||||
|
||||
#include "table/control_codes.h"
|
||||
|
||||
#ifdef WITH_ICU
|
||||
#include <unicode/ustring.h>
|
||||
#endif /* WITH_ICU */
|
||||
|
||||
/**
|
||||
* Construct a new font.
|
||||
* @param size The font size to use for this font.
|
||||
* @param colour The colour to draw this font in.
|
||||
*/
|
||||
Font::Font(FontSize size, TextColour colour) :
|
||||
fc(FontCache::Get(size)), colour(colour)
|
||||
{
|
||||
assert(size < FS_END);
|
||||
}
|
||||
|
||||
#ifdef WITH_ICU
|
||||
/* Implementation details of LEFontInstance */
|
||||
|
||||
le_int32 Font::getUnitsPerEM() const
|
||||
{
|
||||
return this->fc->GetUnitsPerEM();
|
||||
}
|
||||
|
||||
le_int32 Font::getAscent() const
|
||||
{
|
||||
return this->fc->GetAscender();
|
||||
}
|
||||
|
||||
le_int32 Font::getDescent() const
|
||||
{
|
||||
return -this->fc->GetDescender();
|
||||
}
|
||||
|
||||
le_int32 Font::getLeading() const
|
||||
{
|
||||
return this->fc->GetHeight();
|
||||
}
|
||||
|
||||
float Font::getXPixelsPerEm() const
|
||||
{
|
||||
return (float)this->fc->GetHeight();
|
||||
}
|
||||
|
||||
float Font::getYPixelsPerEm() const
|
||||
{
|
||||
return (float)this->fc->GetHeight();
|
||||
}
|
||||
|
||||
float Font::getScaleFactorX() const
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float Font::getScaleFactorY() const
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
const void *Font::getFontTable(LETag tableTag) const
|
||||
{
|
||||
size_t length;
|
||||
return this->getFontTable(tableTag, length);
|
||||
}
|
||||
|
||||
const void *Font::getFontTable(LETag tableTag, size_t &length) const
|
||||
{
|
||||
return this->fc->GetFontTable(tableTag, length);
|
||||
}
|
||||
|
||||
LEGlyphID Font::mapCharToGlyph(LEUnicode32 ch) const
|
||||
{
|
||||
if (IsTextDirectionChar(ch)) return 0;
|
||||
return this->fc->MapCharToGlyph(ch);
|
||||
}
|
||||
|
||||
void Font::getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const
|
||||
{
|
||||
advance.fX = glyph == 0xFFFF ? 0 : this->fc->GetGlyphWidth(glyph);
|
||||
advance.fY = 0;
|
||||
}
|
||||
|
||||
le_bool Font::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
size_t Layouter::AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c)
|
||||
{
|
||||
/* Transform from UTF-32 to internal ICU format of UTF-16. */
|
||||
int32 length = 0;
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
|
||||
return length;
|
||||
}
|
||||
|
||||
ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping)
|
||||
{
|
||||
int32 length = buff_end - buff;
|
||||
|
||||
if (length == 0) {
|
||||
/* ICU's ParagraphLayout cannot handle empty strings, so fake one. */
|
||||
buff[0] = ' ';
|
||||
length = 1;
|
||||
fontMapping.End()[-1].first++;
|
||||
}
|
||||
|
||||
/* Fill ICU's FontRuns with the right data. */
|
||||
FontRuns runs(fontMapping.Length());
|
||||
for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) {
|
||||
runs.add(iter->second, iter->first);
|
||||
}
|
||||
|
||||
LEErrorCode status = LE_NO_ERROR;
|
||||
return new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
|
||||
}
|
||||
|
||||
#else /* WITH_ICU */
|
||||
|
||||
/*** Paragraph layout ***/
|
||||
|
||||
/**
|
||||
* Create the visual run.
|
||||
* @param font The font to use for this run.
|
||||
* @param chars The characters to use for this run.
|
||||
* @param char_count The number of characters in this run.
|
||||
* @param x The initial x position for this run.
|
||||
*/
|
||||
ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_count, int x) :
|
||||
font(font), glyph_count(char_count)
|
||||
{
|
||||
this->glyphs = MallocT<GlyphID>(this->glyph_count);
|
||||
|
||||
/* Positions contains the location of the begin of each of the glyphs, and the end of the last one. */
|
||||
this->positions = MallocT<float>(this->glyph_count * 2 + 2);
|
||||
this->positions[0] = x;
|
||||
this->positions[1] = 0;
|
||||
|
||||
for (int i = 0; i < this->glyph_count; i++) {
|
||||
this->glyphs[i] = font->fc->MapCharToGlyph(chars[i]);
|
||||
this->positions[2 * i + 2] = this->positions[2 * i] + font->fc->GetGlyphWidth(this->glyphs[i]);
|
||||
this->positions[2 * i + 3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Free all data. */
|
||||
ParagraphLayout::VisualRun::~VisualRun()
|
||||
{
|
||||
free(this->positions);
|
||||
free(this->glyphs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the font associated with this run.
|
||||
* @return The font.
|
||||
*/
|
||||
Font *ParagraphLayout::VisualRun::getFont() const
|
||||
{
|
||||
return this->font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of glyhps in this run.
|
||||
* @return The number of glyphs.
|
||||
*/
|
||||
int ParagraphLayout::VisualRun::getGlyphCount() const
|
||||
{
|
||||
return this->glyph_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the glyhps of this run.
|
||||
* @return The glyphs.
|
||||
*/
|
||||
const GlyphID *ParagraphLayout::VisualRun::getGlyphs() const
|
||||
{
|
||||
return this->glyphs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the positions of this run.
|
||||
* @return The positions.
|
||||
*/
|
||||
float *ParagraphLayout::VisualRun::getPositions() const
|
||||
{
|
||||
return this->positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of this font.
|
||||
* @return The height of the font.
|
||||
*/
|
||||
int ParagraphLayout::VisualRun::getLeading() const
|
||||
{
|
||||
return this->getFont()->fc->GetHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the line.
|
||||
* @return The maximum height of the line.
|
||||
*/
|
||||
int ParagraphLayout::Line::getLeading() const
|
||||
{
|
||||
int leading = 0;
|
||||
for (const VisualRun * const *run = this->Begin(); run != this->End(); run++) {
|
||||
leading = max(leading, (*run)->getLeading());
|
||||
}
|
||||
|
||||
return leading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of this line.
|
||||
* @return The width of the line.
|
||||
*/
|
||||
int ParagraphLayout::Line::getWidth() const
|
||||
{
|
||||
if (this->Length() == 0) return 0;
|
||||
|
||||
/*
|
||||
* The last X position of a run contains is the end of that run.
|
||||
* Since there is no left-to-right support, taking this value of
|
||||
* the last run gives us the end of the line and thus the width.
|
||||
*/
|
||||
const VisualRun *run = this->getVisualRun(this->countRuns() - 1);
|
||||
return run->getPositions()[run->getGlyphCount() * 2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of runs in this line.
|
||||
* @return The number of runs.
|
||||
*/
|
||||
int ParagraphLayout::Line::countRuns() const
|
||||
{
|
||||
return this->Length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific visual run.
|
||||
* @return The visual run.
|
||||
*/
|
||||
ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(int run) const
|
||||
{
|
||||
return *this->Get(run);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new paragraph layouter.
|
||||
* @param buffer The characters of the paragraph.
|
||||
* @param length The length of the paragraph.
|
||||
* @param runs The font mapping of this paragraph.
|
||||
*/
|
||||
ParagraphLayout::ParagraphLayout(WChar *buffer, int length, FontMap &runs) : buffer_begin(buffer), buffer(buffer), runs(runs)
|
||||
{
|
||||
assert(runs.End()[-1].first == length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new line with a maximum width.
|
||||
* @param max_width The maximum width of the string.
|
||||
* @return A Line, or NULL when at the end of the paragraph.
|
||||
*/
|
||||
ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width)
|
||||
{
|
||||
/* Simple idea:
|
||||
* - split a line at a newline character, or at a space where we can break a line.
|
||||
* - split for a visual run whenever a new line happens, or the font changes.
|
||||
*/
|
||||
if (this->buffer == NULL) return NULL;
|
||||
|
||||
Line *l = new Line();
|
||||
|
||||
if (*this->buffer == '\0') {
|
||||
/* Only a newline. */
|
||||
this->buffer = NULL;
|
||||
*l->Append() = new VisualRun(this->runs.Begin()->second, this->buffer, 0, 0);
|
||||
return l;
|
||||
}
|
||||
|
||||
const WChar *begin = this->buffer;
|
||||
WChar *last_space = NULL;
|
||||
const WChar *last_char = begin;
|
||||
int width = 0;
|
||||
|
||||
int offset = this->buffer - this->buffer_begin;
|
||||
FontMap::iterator iter = this->runs.Begin();
|
||||
while (iter->first <= offset) {
|
||||
iter++;
|
||||
assert(iter != this->runs.End());
|
||||
}
|
||||
|
||||
const FontCache *fc = iter->second->fc;
|
||||
const WChar *next_run = this->buffer_begin + iter->first + 1;
|
||||
|
||||
for (;;) {
|
||||
WChar c = *this->buffer++;
|
||||
|
||||
if (c == '\0') {
|
||||
this->buffer = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->buffer == next_run) {
|
||||
*l->Append() = new VisualRun(iter->second, begin, this->buffer - begin, l->getWidth());
|
||||
iter++;
|
||||
assert(iter != this->runs.End());
|
||||
|
||||
next_run = this->buffer_begin + iter->first + 1;
|
||||
begin = this->buffer;
|
||||
}
|
||||
|
||||
if (IsWhitespace(c)) last_space = this->buffer;
|
||||
|
||||
last_char = this->buffer;
|
||||
|
||||
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
|
||||
int char_width = GetCharacterWidth(fc->GetSize(), c);
|
||||
width += char_width;
|
||||
if (width > max_width) {
|
||||
/* The string is longer than maximum width so we need to decide
|
||||
* what to do with it. */
|
||||
if (width == char_width) {
|
||||
/* The character is wider than allowed width; don't know
|
||||
* what to do with this case... bail out! */
|
||||
this->buffer = NULL;
|
||||
return l;
|
||||
}
|
||||
|
||||
if (last_space == NULL) {
|
||||
/* No space has been found. Just terminate at our current
|
||||
* location. This usually happens for languages that do not
|
||||
* require spaces in strings, like Chinese, Japanese and
|
||||
* Korean. For other languages terminating mid-word might
|
||||
* not be the best, but terminating the whole string instead
|
||||
* of continuing the word at the next line is worse. */
|
||||
this->buffer--;
|
||||
last_char = this->buffer;
|
||||
} else {
|
||||
/* A space is found; perfect place to terminate */
|
||||
this->buffer = last_space;
|
||||
last_char = last_space - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (l->Length() == 0 || last_char - begin != 0) {
|
||||
*l->Append() = new VisualRun(iter->second, begin, last_char - begin, l->getWidth());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appand a wide character to the internal buffer.
|
||||
* @param buff The buffer to append to.
|
||||
* @param buffer_last The end of the buffer.
|
||||
* @param c The character to add.
|
||||
* @return The number of buffer spaces that were used.
|
||||
*/
|
||||
size_t Layouter::AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c)
|
||||
{
|
||||
*buff = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual ParagraphLayout for the given buffer.
|
||||
* @param buff The begin of the buffer.
|
||||
* @param buff_end The location after the last element in the buffer.
|
||||
* @param fontMapping THe mapping of the fonts.
|
||||
* @return The ParagraphLayout instance.
|
||||
*/
|
||||
ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping)
|
||||
{
|
||||
return new ParagraphLayout(buff, buff_end - buff, fontMapping);
|
||||
}
|
||||
#endif /* !WITH_ICU */
|
||||
|
||||
/**
|
||||
* Create a new layouter.
|
||||
* @param str The string to create the layout for.
|
||||
* @param maxw The maximum width.
|
||||
* @param colour The colour of the font.
|
||||
* @param fontsize The size of font to use.
|
||||
*/
|
||||
Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize)
|
||||
{
|
||||
const CharType *buffer_last = lastof(this->buffer);
|
||||
CharType *buff = this->buffer;
|
||||
|
||||
TextColour cur_colour = colour, prev_colour = colour;
|
||||
WChar c = 0;
|
||||
|
||||
do {
|
||||
Font *f = new Font(fontsize, cur_colour);
|
||||
CharType *buff_begin = buff;
|
||||
FontMap fontMapping;
|
||||
|
||||
/*
|
||||
* Go through the whole string while adding Font instances to the font map
|
||||
* whenever the font changes, and convert the wide characters into a format
|
||||
* usable by ParagraphLayout.
|
||||
*/
|
||||
for (; buff < buffer_last;) {
|
||||
c = Utf8Consume(const_cast<const char **>(&str));
|
||||
if (c == '\0' || c == '\n') {
|
||||
break;
|
||||
} else if (c >= SCC_BLUE && c <= SCC_BLACK) {
|
||||
prev_colour = cur_colour;
|
||||
cur_colour = (TextColour)(c - SCC_BLUE);
|
||||
} else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour.
|
||||
Swap(prev_colour, cur_colour);
|
||||
} else if (c == SCC_TINYFONT) {
|
||||
fontsize = FS_SMALL;
|
||||
} else if (c == SCC_BIGFONT) {
|
||||
fontsize = FS_LARGE;
|
||||
} else {
|
||||
buff += AppendToBuffer(buff, buffer_last, c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fontMapping.Contains(buff - buff_begin)) {
|
||||
fontMapping.Insert(buff - buff_begin, f);
|
||||
*this->fonts.Append() = f;
|
||||
} else {
|
||||
delete f;
|
||||
}
|
||||
f = new Font(fontsize, cur_colour);
|
||||
}
|
||||
|
||||
/* Better safe than sorry. */
|
||||
*buff = '\0';
|
||||
|
||||
if (!fontMapping.Contains(buff - buff_begin)) {
|
||||
fontMapping.Insert(buff - buff_begin, f);
|
||||
*this->fonts.Append() = f;
|
||||
}
|
||||
ParagraphLayout *p = GetParagraphLayout(buff_begin, buff, fontMapping);
|
||||
|
||||
/* Copy all lines into a local cache so we can reuse them later on more easily. */
|
||||
ParagraphLayout::Line *l;
|
||||
while ((l = p->nextLine(maxw)) != NULL) {
|
||||
*this->Append() = l;
|
||||
}
|
||||
|
||||
delete p;
|
||||
|
||||
} while (c != '\0' && buff < buffer_last);
|
||||
}
|
||||
|
||||
/** Free everything we allocated. */
|
||||
Layouter::~Layouter()
|
||||
{
|
||||
for (Font **iter = this->fonts.Begin(); iter != this->fonts.End(); iter++) {
|
||||
delete *iter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boundaries of this paragraph.
|
||||
* @return The boundaries.
|
||||
*/
|
||||
Dimension Layouter::GetBounds()
|
||||
{
|
||||
Dimension d = { 0, 0 };
|
||||
for (ParagraphLayout::Line **l = this->Begin(); l != this->End(); l++) {
|
||||
d.width = max<uint>(d.width, (*l)->getWidth());
|
||||
d.height += (*l)->getLeading();
|
||||
}
|
||||
return d;
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file gfx_layout.h Functions related to laying out the texts. */
|
||||
|
||||
#ifndef GFX_LAYOUT_H
|
||||
#define GFX_LAYOUT_H
|
||||
|
||||
#include "fontcache.h"
|
||||
#include "gfx_func.h"
|
||||
#include "core/smallmap_type.hpp"
|
||||
|
||||
#ifdef WITH_ICU
|
||||
#include "layout/ParagraphLayout.h"
|
||||
#define ICU_FONTINSTANCE : public LEFontInstance
|
||||
#else /* WITH_ICU */
|
||||
#define ICU_FONTINSTANCE
|
||||
#endif /* WITH_ICU */
|
||||
|
||||
/**
|
||||
* Container with information about a font.
|
||||
*/
|
||||
class Font ICU_FONTINSTANCE {
|
||||
public:
|
||||
FontCache *fc; ///< The font we are using.
|
||||
TextColour colour; ///< The colour this font has to be.
|
||||
|
||||
Font(FontSize size, TextColour colour);
|
||||
|
||||
#ifdef WITH_ICU
|
||||
/* Implementation details of LEFontInstance */
|
||||
|
||||
le_int32 getUnitsPerEM() const;
|
||||
le_int32 getAscent() const;
|
||||
le_int32 getDescent() const;
|
||||
le_int32 getLeading() const;
|
||||
float getXPixelsPerEm() const;
|
||||
float getYPixelsPerEm() const;
|
||||
float getScaleFactorX() const;
|
||||
float getScaleFactorY() const;
|
||||
const void *getFontTable(LETag tableTag) const;
|
||||
const void *getFontTable(LETag tableTag, size_t &length) const;
|
||||
LEGlyphID mapCharToGlyph(LEUnicode32 ch) const;
|
||||
void getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const;
|
||||
le_bool getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const;
|
||||
#endif /* WITH_ICU */
|
||||
};
|
||||
|
||||
/** Mapping from index to font. */
|
||||
typedef SmallMap<int, Font *> FontMap;
|
||||
|
||||
#ifndef WITH_ICU
|
||||
/**
|
||||
* Class handling the splitting of a paragraph of text into lines and
|
||||
* visual runs.
|
||||
*
|
||||
* One constructs this class with the text that needs to be split into
|
||||
* lines. Then nextLine is called with the maximum width until NULL is
|
||||
* returned. Each nextLine call creates VisualRuns which contain the
|
||||
* length of text that are to be drawn with the same font. In other
|
||||
* words, the result of this class is a list of sub strings with their
|
||||
* font. The sub strings are then already fully laid out, and only
|
||||
* need actual drawing.
|
||||
*
|
||||
* The positions in a visual run are sequential pairs of X,Y of the
|
||||
* begin of each of the glyphs plus an extra pair to mark the end.
|
||||
*
|
||||
* @note This variant does not handle left-to-right properly. This
|
||||
* is supported in the one ParagraphLayout coming from ICU.
|
||||
* @note Does not conform to function naming style as it provides a
|
||||
* fallback for the ICU class.
|
||||
*/
|
||||
class ParagraphLayout {
|
||||
public:
|
||||
/** Visual run contains data about the bit of text with the same font. */
|
||||
class VisualRun {
|
||||
Font *font; ///< The font used to layout these.
|
||||
GlyphID *glyphs; ///< The glyphs we're drawing.
|
||||
float *positions; ///< The positions of the glyphs.
|
||||
int glyph_count; ///< The number of glyphs.
|
||||
|
||||
public:
|
||||
VisualRun(Font *font, const WChar *chars, int glyph_count, int x);
|
||||
~VisualRun();
|
||||
Font *getFont() const;
|
||||
int getGlyphCount() const;
|
||||
const GlyphID *getGlyphs() const;
|
||||
float *getPositions() const;
|
||||
int getLeading() const;
|
||||
};
|
||||
|
||||
/** A single line worth of VisualRuns. */
|
||||
class Line : public AutoDeleteSmallVector<VisualRun *, 4> {
|
||||
public:
|
||||
int getLeading() const;
|
||||
int getWidth() const;
|
||||
int countRuns() const;
|
||||
VisualRun *getVisualRun(int run) const;
|
||||
};
|
||||
|
||||
const WChar *buffer_begin; ///< Begin of the buffer.
|
||||
WChar *buffer; ///< The current location in the buffer.
|
||||
FontMap &runs; ///< The fonts we have to use for this paragraph.
|
||||
|
||||
ParagraphLayout(WChar *buffer, int length, FontMap &runs);
|
||||
Line *nextLine(int max_width);
|
||||
};
|
||||
#endif /* !WITH_ICU */
|
||||
|
||||
/**
|
||||
* The layouter performs all the layout work.
|
||||
*
|
||||
* It also accounts for the memory allocations and frees.
|
||||
*/
|
||||
class Layouter : public AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
|
||||
#ifdef WITH_ICU
|
||||
typedef UChar CharType; ///< The type of character used within the layouter.
|
||||
#else /* WITH_ICU */
|
||||
typedef WChar CharType; ///< The type of character used within the layouter.
|
||||
#endif /* WITH_ICU */
|
||||
|
||||
size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c);
|
||||
ParagraphLayout *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping);
|
||||
|
||||
CharType buffer[DRAW_STRING_BUFFER]; ///< Buffer for the text that is going to be drawn.
|
||||
SmallVector<Font *, 4> fonts; ///< The fonts needed for drawing.
|
||||
|
||||
public:
|
||||
Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);
|
||||
~Layouter();
|
||||
Dimension GetBounds();
|
||||
};
|
||||
|
||||
#endif /* GFX_LAYOUT_H */
|
|
@ -81,10 +81,7 @@ static inline uint GetChatMessageCount()
|
|||
void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *message, ...)
|
||||
{
|
||||
char buf[DRAW_STRING_BUFFER];
|
||||
const char *bufp;
|
||||
va_list va;
|
||||
uint msg_count;
|
||||
uint16 lines;
|
||||
|
||||
va_start(va, message);
|
||||
vsnprintf(buf, lengthof(buf), message, va);
|
||||
|
@ -92,29 +89,16 @@ void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *m
|
|||
|
||||
Utf8TrimString(buf, DRAW_STRING_BUFFER);
|
||||
|
||||
/* Force linebreaks for strings that are too long */
|
||||
lines = GB(FormatStringLinebreaks(buf, lastof(buf), _chatmsg_box.width - 8), 0, 16) + 1;
|
||||
if (lines >= MAX_CHAT_MESSAGES) return;
|
||||
|
||||
msg_count = GetChatMessageCount();
|
||||
/* We want to add more chat messages than there is free space for, remove 'old' */
|
||||
if (lines > MAX_CHAT_MESSAGES - msg_count) {
|
||||
int i = lines - (MAX_CHAT_MESSAGES - msg_count);
|
||||
memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
|
||||
msg_count = MAX_CHAT_MESSAGES - lines;
|
||||
uint msg_count = GetChatMessageCount();
|
||||
if (MAX_CHAT_MESSAGES == msg_count) {
|
||||
memmove(&_chatmsg_list[0], &_chatmsg_list[1], sizeof(_chatmsg_list[0]) * (msg_count - 1));
|
||||
msg_count = MAX_CHAT_MESSAGES - 1;
|
||||
}
|
||||
|
||||
for (bufp = buf; lines != 0; lines--) {
|
||||
ChatMessage *cmsg = &_chatmsg_list[msg_count++];
|
||||
strecpy(cmsg->message, bufp, lastof(cmsg->message));
|
||||
|
||||
/* The default colour for a message is company colour. Replace this with
|
||||
* white for any additional lines */
|
||||
cmsg->colour = (bufp == buf && (colour & TC_IS_PALETTE_COLOUR)) ? colour : TC_WHITE;
|
||||
cmsg->remove_time = _realtime_tick + duration * 1000;
|
||||
|
||||
bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
|
||||
}
|
||||
ChatMessage *cmsg = &_chatmsg_list[msg_count++];
|
||||
strecpy(cmsg->message, buf, lastof(cmsg->message));
|
||||
cmsg->colour = (colour & TC_IS_PALETTE_COLOUR) ? colour : TC_WHITE;
|
||||
cmsg->remove_time = _realtime_tick + duration * 1000;
|
||||
|
||||
_chatmessage_dirty = true;
|
||||
}
|
||||
|
@ -246,18 +230,27 @@ void NetworkDrawChatMessage()
|
|||
|
||||
_cur_dpi = &_screen; // switch to _screen painting
|
||||
|
||||
int string_height = 0;
|
||||
for (uint i = 0; i < count; i++) {
|
||||
SetDParamStr(0, _chatmsg_list[i].message);
|
||||
string_height += GetStringLineCount(STR_JUST_RAW_STRING, width - 1) * FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING;
|
||||
}
|
||||
|
||||
string_height = min(string_height, MAX_CHAT_MESSAGES * (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING));
|
||||
|
||||
int top = _screen.height - _chatmsg_box.y - string_height - 2;
|
||||
int bottom = _screen.height - _chatmsg_box.y - 2;
|
||||
/* Paint a half-transparent box behind the chat messages */
|
||||
GfxFillRect(
|
||||
_chatmsg_box.x,
|
||||
_screen.height - _chatmsg_box.y - count * (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING) - 2,
|
||||
_chatmsg_box.x + _chatmsg_box.width - 1,
|
||||
_screen.height - _chatmsg_box.y - 2,
|
||||
GfxFillRect(_chatmsg_box.x, top - 2, _chatmsg_box.x + _chatmsg_box.width - 1, bottom,
|
||||
PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR // black, but with some alpha for background
|
||||
);
|
||||
|
||||
/* Paint the chat messages starting with the lowest at the bottom */
|
||||
for (uint y = FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING; count-- != 0; y += (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING)) {
|
||||
DrawString(_chatmsg_box.x + 3, _chatmsg_box.x + _chatmsg_box.width - 1, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].message, _chatmsg_list[count].colour);
|
||||
int ypos = bottom - 2;
|
||||
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
ypos = DrawStringMultiLine(_chatmsg_box.x + 3, _chatmsg_box.x + _chatmsg_box.width - 1, top, ypos, _chatmsg_list[i].message, _chatmsg_list[i].colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - NETWORK_CHAT_LINE_SPACING;
|
||||
if (ypos < top) break;
|
||||
}
|
||||
|
||||
/* Make sure the data is updated next flush */
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#define WindowClass OTTDWindowClass
|
||||
#define ScriptOrder OTTDScriptOrder
|
||||
#define Palette OTTDPalette
|
||||
#define GlyphID OTTDGlyphID
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
@ -53,6 +54,7 @@
|
|||
#undef WindowClass
|
||||
#undef ScriptOrder
|
||||
#undef Palette
|
||||
#undef GlyphID
|
||||
|
||||
/* remove the variables that CoreServices defines, but we define ourselves too */
|
||||
#undef bool
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "newgrf_text.h"
|
||||
#include "fileio_func.h"
|
||||
#include "signs_base.h"
|
||||
#include "fontcache.h"
|
||||
#include "fontdetection.h"
|
||||
#include "error.h"
|
||||
#include "strings_func.h"
|
||||
#include "rev.h"
|
||||
|
@ -2073,9 +2073,9 @@ class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
|
|||
/* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
|
||||
{
|
||||
#ifdef WITH_FREETYPE
|
||||
strecpy(settings->small_font, font_name, lastof(settings->small_font));
|
||||
strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
|
||||
strecpy(settings->large_font, font_name, lastof(settings->large_font));
|
||||
strecpy(settings->small.font, font_name, lastof(settings->small.font));
|
||||
strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
|
||||
strecpy(settings->large.font, font_name, lastof(settings->large.font));
|
||||
#endif /* WITH_FREETYPE */
|
||||
}
|
||||
};
|
||||
|
|
|
@ -135,36 +135,36 @@ def = false
|
|||
ifdef = WITH_FREETYPE
|
||||
name = ""small_font""
|
||||
type = SLE_STRB
|
||||
var = _freetype.small_font
|
||||
var = _freetype.small.font
|
||||
def = NULL
|
||||
|
||||
[SDTG_STR]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""medium_font""
|
||||
type = SLE_STRB
|
||||
var = _freetype.medium_font
|
||||
var = _freetype.medium.font
|
||||
def = NULL
|
||||
|
||||
[SDTG_STR]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""large_font""
|
||||
type = SLE_STRB
|
||||
var = _freetype.large_font
|
||||
var = _freetype.large.font
|
||||
def = NULL
|
||||
|
||||
[SDTG_STR]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""mono_font""
|
||||
type = SLE_STRB
|
||||
var = _freetype.mono_font
|
||||
var = _freetype.mono.font
|
||||
def = NULL
|
||||
|
||||
[SDTG_VAR]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""small_size""
|
||||
type = SLE_UINT
|
||||
var = _freetype.small_size
|
||||
def = 8
|
||||
var = _freetype.small.size
|
||||
def = 0
|
||||
min = 0
|
||||
max = 72
|
||||
|
||||
|
@ -172,8 +172,8 @@ max = 72
|
|||
ifdef = WITH_FREETYPE
|
||||
name = ""medium_size""
|
||||
type = SLE_UINT
|
||||
var = _freetype.medium_size
|
||||
def = 10
|
||||
var = _freetype.medium.size
|
||||
def = 0
|
||||
min = 0
|
||||
max = 72
|
||||
|
||||
|
@ -181,8 +181,8 @@ max = 72
|
|||
ifdef = WITH_FREETYPE
|
||||
name = ""large_size""
|
||||
type = SLE_UINT
|
||||
var = _freetype.large_size
|
||||
def = 16
|
||||
var = _freetype.large.size
|
||||
def = 0
|
||||
min = 0
|
||||
max = 72
|
||||
|
||||
|
@ -190,33 +190,33 @@ max = 72
|
|||
ifdef = WITH_FREETYPE
|
||||
name = ""mono_size""
|
||||
type = SLE_UINT
|
||||
var = _freetype.mono_size
|
||||
def = 10
|
||||
var = _freetype.mono.size
|
||||
def = 0
|
||||
min = 0
|
||||
max = 72
|
||||
|
||||
[SDTG_BOOL]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""small_aa""
|
||||
var = _freetype.small_aa
|
||||
var = _freetype.small.aa
|
||||
def = false
|
||||
|
||||
[SDTG_BOOL]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""medium_aa""
|
||||
var = _freetype.medium_aa
|
||||
var = _freetype.medium.aa
|
||||
def = false
|
||||
|
||||
[SDTG_BOOL]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""large_aa""
|
||||
var = _freetype.large_aa
|
||||
var = _freetype.large.aa
|
||||
def = false
|
||||
|
||||
[SDTG_BOOL]
|
||||
ifdef = WITH_FREETYPE
|
||||
name = ""mono_aa""
|
||||
var = _freetype.mono_aa
|
||||
var = _freetype.mono.aa
|
||||
def = false
|
||||
|
||||
[SDTG_VAR]
|
||||
|
|
|
@ -135,7 +135,7 @@ TextfileWindow::TextfileWindow(TextfileType file_type) : Window(), file_type(fil
|
|||
/* virtual */ void TextfileWindow::SetFontNames(FreeTypeSettings *settings, const char *font_name)
|
||||
{
|
||||
#ifdef WITH_FREETYPE
|
||||
strecpy(settings->mono_font, font_name, lastof(settings->mono_font));
|
||||
strecpy(settings->mono.font, font_name, lastof(settings->mono.font));
|
||||
#endif /* WITH_FREETYPE */
|
||||
}
|
||||
|
||||
|
|
|
@ -501,7 +501,7 @@ static inline void DrawCloseBox(const Rect &r, Colours colour, StringID str)
|
|||
{
|
||||
assert(str == STR_BLACK_CROSS || str == STR_SILVER_CROSS); // black or silver cross
|
||||
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
|
||||
DrawString(r.left + WD_CLOSEBOX_LEFT, r.right - WD_CLOSEBOX_RIGHT, r.top + WD_CLOSEBOX_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
|
||||
DrawString(r.left, r.right, r.top + WD_CLOSEBOX_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue