Provide windows font host implementation needed to support TrueType text in pdf backend.

- Move AdvanceMetric template functions into new file SkAdvancedTypefaceMetrics.cpp

Review URL: http://codereview.appspot.com/4174041

git-svn-id: http://skia.googlecode.com/svn/trunk@789 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
vandebo@chromium.org 2011-02-14 23:19:59 +00:00
parent 97b624529c
commit 6f72d1eacd
6 changed files with 346 additions and 97 deletions

View File

@ -107,4 +107,30 @@ public:
SkTScopedPtr<SkAutoTArray<SkString> > fGlyphNames;
};
namespace skia_advanced_typeface_metrics_utils {
template <typename Data>
void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
int startId);
template <typename Data>
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
int startId);
template <typename Data>
void finishRange(
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
int endId,
typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
type);
template <typename Data, typename FontHandle>
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
FontHandle fontHandle,
int num_glyphs,
bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data));
} // namespace skia_advanced_typeface_metrics_utils
#endif

View File

@ -0,0 +1,158 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SkAdvancedTypefaceMetrics.h"
#include "SkTypes.h"
#ifdef SK_BUILD_FOR_UNIX
#include <ft2build.h>
#include FT_FREETYPE_H
#endif
namespace skia_advanced_typeface_metrics_utils {
template <typename Data>
void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
int startId) {
range->fStartId = startId;
range->fAdvance.setCount(0);
}
template <typename Data>
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
int startId) {
nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric<Data>);
resetRange(nextSlot->get(), startId);
return nextSlot->get();
}
template <typename Data>
void finishRange(
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
int endId,
typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
type) {
range->fEndId = endId;
range->fType = type;
int newLength;
if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange) {
newLength = endId - range->fStartId + 1;
} else {
newLength = 1;
}
SkASSERT(range->fAdvance.count() >= newLength);
range->fAdvance.setCount(newLength);
}
template <typename Data, typename FontHandle>
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
FontHandle fontHandle,
int num_glyphs,
bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) {
// Assuming that an ASCII representation of a width or a glyph id is,
// on average, 3 characters long gives the following cut offs for
// using different range types:
// When currently in a range
// - Removing 4 0's is a win
// - Removing 5 repeats is a win
// When not currently in a range
// - Removing 1 0 is a win
// - Removing 3 repeats is a win
SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result;
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange;
curRange = appendRange(&result, 0);
Data lastAdvance = SK_MinS16;
int repeats = 0;
for (int gId = 0; gId < num_glyphs; gId++) {
Data advance;
if (!getAdvance(fontHandle, gId, &advance)) {
num_glyphs = (gId > 0) ? gId - 1 : 0;
break;
}
if (advance == lastAdvance) {
repeats++;
} else if (curRange->fAdvance.count() == repeats + 1) {
if (lastAdvance == 0 && repeats >= 0) {
resetRange(curRange, gId);
} else if (repeats >= 2) {
finishRange(curRange, gId - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRun);
curRange = appendRange(&curRange->fNext, gId);
}
repeats = 0;
} else {
if (lastAdvance == 0 && repeats >= 3) {
finishRange(curRange, gId - repeats - 2,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
curRange = appendRange(&curRange->fNext, gId);
} else if (repeats >= 4) {
finishRange(curRange, gId - repeats - 2,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
curRange = appendRange(&curRange->fNext, gId - repeats - 1);
curRange->fAdvance.append(1, &lastAdvance);
finishRange(curRange, gId - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRun);
curRange = appendRange(&curRange->fNext, gId);
}
repeats = 0;
}
curRange->fAdvance.append(1, &advance);
lastAdvance = advance;
}
finishRange(curRange, num_glyphs - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
return result.release();
}
// Make AdvanceMetric template functions available for linking with typename
// WidthRange and VerticalAdvanceRange.
#ifdef SK_BUILD_FOR_WIN
template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
HDC hdc,
int num_glyphs,
bool (*getAdvance)(HDC hdc, int gId, int16_t* data));
#elif SK_BUILD_FOR_UNIX
template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
FT_Face face,
int num_glyphs,
bool (*getAdvance)(FT_Face face, int gId, int16_t* data));
#endif
template void resetRange(
SkAdvancedTypefaceMetrics::WidthRange* range,
int startId);
template SkAdvancedTypefaceMetrics::WidthRange* appendRange(
SkTScopedPtr<SkAdvancedTypefaceMetrics::WidthRange >* nextSlot,
int startId);
template void finishRange<int16_t>(
SkAdvancedTypefaceMetrics::WidthRange* range,
int endId,
SkAdvancedTypefaceMetrics::WidthRange::MetricType type);
template void resetRange(
SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range,
int startId);
template SkAdvancedTypefaceMetrics::VerticalAdvanceRange* appendRange(
SkTScopedPtr<SkAdvancedTypefaceMetrics::VerticalAdvanceRange >*
nextSlot,
int startId);
template void finishRange<SkAdvancedTypefaceMetrics::VerticalMetric>(
SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range,
int endId,
SkAdvancedTypefaceMetrics::VerticalAdvanceRange::MetricType type);
} // namespace skia_advanced_typeface_metrics_utils

View File

@ -1,5 +1,6 @@
SOURCE := \
Sk64.cpp \
SkAdvancedTypefaceMetrics.cpp \
SkAlphaRuns.cpp \
SkBitmap.cpp \
SkBitmapProcShader.cpp \

View File

@ -73,6 +73,8 @@
#define SkASSERT_CONTINUE(pred)
#endif
using namespace skia_advanced_typeface_metrics_utils;
//////////////////////////////////////////////////////////////////////////
struct SkFaceRec;
@ -331,98 +333,14 @@ static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) {
return true;
}
static int16_t getWidthAdvance(FT_Face face, int gId) {
static bool getWidthAdvance(FT_Face face, int gId, int16_t* data) {
FT_Fixed advance = 0;
SkAssertResult(getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance) == 0);
return advance;
}
template <typename Data>
static void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
int startId) {
range->fStartId = startId;
range->fAdvance.setCount(0);
}
template <typename Data>
static SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
int startId) {
nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric<Data>);
resetRange(nextSlot->get(), startId);
return nextSlot->get();
}
template <typename Data>
static void finishRange(
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
int endId,
typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
type) {
range->fEndId = endId;
range->fType = type;
int newLength;
if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange)
newLength = endId - range->fStartId + 1;
else
newLength = 1;
SkASSERT(range->fAdvance.count() >= newLength);
range->fAdvance.setCount(newLength);
}
template <typename Data>
static SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
FT_Face face, Data (*getAdvance)(FT_Face face, int gId)) {
// Assuming that an ASCII representation of a width or a glyph id is,
// on average, 3 characters long gives the following cut offs for
// using different range types:
// When currently in a range
// - Removing 4 0's is a win
// - Removing 5 repeats is a win
// When not currently in a range
// - Removing 1 0 is a win
// - Removing 3 repeats is a win
SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result;
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange;
curRange = appendRange(&result, 0);
Data lastAdvance = SHRT_MIN;
int repeats = 0;
for (int gId = 0; gId < face->num_glyphs; gId++) {
Data advance = getAdvance(face, gId);
if (advance == lastAdvance) {
repeats++;
} else if (curRange->fAdvance.count() == repeats + 1) {
if (lastAdvance == 0 && repeats >= 0) {
resetRange(curRange, gId);
} else if (repeats >= 2) {
finishRange(curRange, gId - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRun);
curRange = appendRange(&curRange->fNext, gId);
}
repeats = 0;
} else {
if (lastAdvance == 0 && repeats >= 3) {
finishRange(curRange, gId - repeats - 2,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
curRange = appendRange(&curRange->fNext, gId);
} else if (repeats >= 4) {
finishRange(curRange, gId - repeats - 2,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
curRange = appendRange(&curRange->fNext, gId - repeats - 1);
curRange->fAdvance.append(1, &lastAdvance);
finishRange(curRange, gId - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRun);
curRange = appendRange(&curRange->fNext, gId);
}
repeats = 0;
}
curRange->fAdvance.append(1, &advance);
lastAdvance = advance;
if (getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance)) {
return false;
}
finishRange(curRange, face->num_glyphs - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
return result.release();
SkASSERT(data);
*data = advance;
return true;
}
// static
@ -499,6 +417,7 @@ SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
// Figure out a good guess for StemV - Min width of i, I, !, 1.
// This probably isn't very good with an italic font.
int16_t min_width = SHRT_MAX;
info->fStemV = 0;
char stem_chars[] = {'i', 'I', '!', '1'};
for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
FT_BBox bbox;
@ -550,7 +469,7 @@ SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
info->fGlyphWidths->fAdvance.append(1, &advance);
finishRange(info->fGlyphWidths.get(), 0,
SkAdvancedTypefaceMetrics::WidthRange::kDefault);
} else if(!cid) {
} else if (!cid) {
appendRange(&info->fGlyphWidths, 0);
// So as to not blow out the stack, get advances in batches.
for (int gID = 0; gID < face->num_glyphs; gID += 128) {
@ -568,7 +487,8 @@ SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1,
SkAdvancedTypefaceMetrics::WidthRange::kRange);
} else {
info->fGlyphWidths.reset(getAdvanceData(face, &getWidthAdvance));
info->fGlyphWidths.reset(
getAdvanceData(face, face->num_glyphs, &getWidthAdvance));
}
if (info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {

151
src/ports/SkFontHost_win.cpp Normal file → Executable file
View File

@ -19,6 +19,8 @@
#include "SkFontHost.h"
#include "SkDescriptor.h"
#include "SkAdvancedTypefaceMetrics.h"
#include "SkStream.h"
#include "SkThread.h"
#ifdef WIN32
@ -28,6 +30,8 @@
// client3d has to undefine this for now
#define CAN_USE_LOGFONT_NAME
using namespace skia_advanced_typeface_metrics_utils;
static SkMutex gFTMutex;
static const uint16_t BUFFERSIZE = (16384 - 32);
@ -456,11 +460,133 @@ SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
return NULL;
}
static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
// Initialize the MAT2 structure to the identify transformation matrix.
static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
SkScalarToFIXED(0), SkScalarToFIXED(1)};
int flags = GGO_METRICS | GGO_GLYPH_INDEX;
GLYPHMETRICS gm;
if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
return false;
}
SkASSERT(advance);
*advance = gm.gmCellIncX;
return true;
}
// static
SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
uint32_t fontID, bool perGlyphInfo) {
SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
return NULL;
SkAutoMutexAcquire ac(gFTMutex);
LogFontTypeface* rec = LogFontTypeface::FindById(fontID);
LOGFONT lf = rec->logFont();
SkAdvancedTypefaceMetrics* info = NULL;
HDC hdc = CreateCompatibleDC(NULL);
HFONT font = CreateFontIndirect(&lf);
HFONT savefont = (HFONT)SelectObject(hdc, font);
HFONT designFont = NULL;
// To request design units, create a logical font whose height is specified
// as unitsPerEm.
OUTLINETEXTMETRIC otm;
if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||
!GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
goto Error;
}
lf.lfHeight = -SkToS32(otm.otmEMSquare);
designFont = CreateFontIndirect(&lf);
SelectObject(hdc, designFont);
if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
goto Error;
}
info = new SkAdvancedTypefaceMetrics;
#ifdef UNICODE
// Get the buffer size needed first.
size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
0, NULL, NULL);
// Allocate a buffer (str_len already has terminating null accounted for).
char *familyName = new char[str_len];
// Now actually convert the string.
WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
NULL, NULL);
info->fFontName.set(familyName);
delete [] familyName;
#else
info->fFontName.set(lf.lfFaceName);
#endif
if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
} else {
info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
}
info->fEmSize = otm.otmEMSquare;
info->fMultiMaster = false;
info->fStyle = 0;
// If this bit is clear the font is a fixed pitch font.
if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
}
if (otm.otmTextMetrics.tmItalic) {
info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
}
// Setting symbolic style by default for now.
info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
} else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
}
// The main italic angle of the font, in tenths of a degree counterclockwise
// from vertical.
info->fItalicAngle = otm.otmItalicAngle / 10;
info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
// TODO(ctguil): Use alternate cap height calculation.
// MSDN says otmsCapEmHeight is not support but it is returning a value on
// my Win7 box.
info->fCapHeight = otm.otmsCapEmHeight;
info->fBBox =
SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
// Figure out a good guess for StemV - Min width of i, I, !, 1.
// This probably isn't very good with an italic font.
int16_t min_width = SHRT_MAX;
info->fStemV = 0;
char stem_chars[] = {'i', 'I', '!', '1'};
for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
ABC abcWidths;
if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
int16_t width = abcWidths.abcB;
if (width > 0 && width < min_width) {
min_width = width;
info->fStemV = min_width;
}
}
}
// If bit 1 is set, the font may not be embedded in a document.
// If bit 1 is clear, the font can be embedded.
// If bit 2 is set, the embedding is read-only.
if (otm.otmfsType & 0x1) {
info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
} else if (perGlyphInfo) {
info->fGlyphWidths.reset(
getAdvanceData(hdc, SHRT_MAX, &getWidthAdvance));
}
Error:
SelectObject(hdc, savefont);
DeleteObject(designFont);
DeleteObject(font);
DeleteDC(hdc);
return info;
}
SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
@ -471,8 +597,25 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
}
SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
SkASSERT(!"SkFontHost::OpenStream unimplemented");
return NULL;
SkAutoMutexAcquire ac(gFTMutex);
LogFontTypeface* rec = LogFontTypeface::FindById(uniqueID);
HDC hdc = ::CreateCompatibleDC(NULL);
HFONT font = CreateFontIndirect(&rec->logFont());
HFONT savefont = (HFONT)SelectObject(hdc, font);
size_t bufferSize = GetFontData(hdc, 0, 0, NULL, 0);
SkMemoryStream* stream = new SkMemoryStream(bufferSize);
if (!GetFontData(hdc, 0, 0, (void*)stream->getMemoryBase(), bufferSize)) {
delete stream;
stream = NULL;
}
SelectObject(hdc, savefont);
DeleteObject(font);
DeleteDC(hdc);
return stream;
}
SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {

View File

@ -293,6 +293,7 @@
<ClCompile Include="..\..\samplecode\SampleVertices.cpp" />
<ClCompile Include="..\..\samplecode\SampleXfermodes.cpp" />
<ClCompile Include="..\..\src\core\Sk64.cpp" />
<ClCompile Include="..\..\src\core\SkAdvancedTypefaceMetrics.cpp" />
<ClCompile Include="..\..\src\core\SkAlphaRuns.cpp" />
<ClCompile Include="..\..\src\core\SkBitmap.cpp" />
<ClCompile Include="..\..\src\core\SkBitmapProcShader.cpp" />
@ -476,4 +477,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>