blob: 9b8cdb482b030b5d1b2438df71a50b4f6a6dd73c [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/pepper/pepper_truetype_font.h"
#include <windows.h>
#include <set>
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
#include "base/win/scoped_hdc.h"
#include "base/win/scoped_select_object.h"
#include "content/public/renderer/render_thread.h"
#include "ppapi/c/dev/ppb_truetype_font_dev.h"
#include "ppapi/c/pp_errors.h"
namespace content {
namespace {
class PepperTrueTypeFontWin : public PepperTrueTypeFont {
public:
explicit PepperTrueTypeFontWin(
const ppapi::proxy::SerializedTrueTypeFontDesc& desc);
virtual ~PepperTrueTypeFontWin();
// PepperTrueTypeFont overrides.
virtual bool IsValid() OVERRIDE;
virtual int32_t Describe(ppapi::proxy::SerializedTrueTypeFontDesc* desc)
OVERRIDE;
virtual int32_t GetTableTags(std::vector<uint32_t>* tags) OVERRIDE;
virtual int32_t GetTable(uint32_t table_tag,
int32_t offset,
int32_t max_data_length,
std::string* data) OVERRIDE;
private:
DWORD GetFontData(HDC hdc,
DWORD table,
DWORD offset,
LPVOID buffer,
DWORD length);
HFONT font_;
DISALLOW_COPY_AND_ASSIGN(PepperTrueTypeFontWin);
};
PepperTrueTypeFontWin::PepperTrueTypeFontWin(
const ppapi::proxy::SerializedTrueTypeFontDesc& desc) {
DWORD pitch_and_family = DEFAULT_PITCH;
switch (desc.generic_family) {
case PP_TRUETYPEFONTFAMILY_SERIF:
pitch_and_family |= FF_ROMAN;
break;
case PP_TRUETYPEFONTFAMILY_SANSSERIF:
pitch_and_family |= FF_SWISS;
break;
case PP_TRUETYPEFONTFAMILY_CURSIVE:
pitch_and_family |= FF_SCRIPT;
break;
case PP_TRUETYPEFONTFAMILY_FANTASY:
pitch_and_family |= FF_DECORATIVE;
break;
case PP_TRUETYPEFONTFAMILY_MONOSPACE:
pitch_and_family |= FF_MODERN;
break;
}
// TODO(bbudge) support widths (extended, condensed).
font_ = CreateFont(0 /* height */,
0 /* width */,
0 /* escapement */,
0 /* orientation */,
desc.weight, // our weight enum matches Windows.
(desc.style & PP_TRUETYPEFONTSTYLE_ITALIC) ? 1 : 0,
0 /* underline */,
0 /* strikeout */,
desc.charset, // our charset enum matches Windows.
OUT_OUTLINE_PRECIS, // truetype and other outline fonts
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
pitch_and_family,
base::UTF8ToUTF16(desc.family).c_str());
}
PepperTrueTypeFontWin::~PepperTrueTypeFontWin() {}
bool PepperTrueTypeFontWin::IsValid() { return font_ != NULL; }
int32_t PepperTrueTypeFontWin::Describe(
ppapi::proxy::SerializedTrueTypeFontDesc* desc) {
LOGFONT font_desc;
if (!::GetObject(font_, sizeof(LOGFONT), &font_desc))
return PP_ERROR_FAILED;
switch (font_desc.lfPitchAndFamily & 0xF0) { // Top 4 bits are family.
case FF_ROMAN:
desc->generic_family = PP_TRUETYPEFONTFAMILY_SERIF;
break;
case FF_SWISS:
desc->generic_family = PP_TRUETYPEFONTFAMILY_SANSSERIF;
break;
case FF_SCRIPT:
desc->generic_family = PP_TRUETYPEFONTFAMILY_CURSIVE;
break;
case FF_DECORATIVE:
desc->generic_family = PP_TRUETYPEFONTFAMILY_FANTASY;
break;
case FF_MODERN:
desc->generic_family = PP_TRUETYPEFONTFAMILY_MONOSPACE;
break;
}
desc->style = font_desc.lfItalic ? PP_TRUETYPEFONTSTYLE_ITALIC
: PP_TRUETYPEFONTSTYLE_NORMAL;
desc->weight = static_cast<PP_TrueTypeFontWeight_Dev>(font_desc.lfWeight);
desc->width = PP_TRUETYPEFONTWIDTH_NORMAL;
desc->charset = static_cast<PP_TrueTypeFontCharset_Dev>(font_desc.lfCharSet);
// To get the face name, select the font and query for the name. GetObject
// doesn't fill in the name field of the LOGFONT structure.
base::win::ScopedCreateDC hdc(::CreateCompatibleDC(NULL));
if (hdc) {
base::win::ScopedSelectObject select_object(hdc, font_);
WCHAR name[LF_FACESIZE];
GetTextFace(hdc, LF_FACESIZE, name);
desc->family = base::UTF16ToUTF8(name);
}
return PP_OK;
}
DWORD PepperTrueTypeFontWin::GetFontData(HDC hdc,
DWORD table,
DWORD offset,
void* buffer,
DWORD length) {
// If this is a zero byte read, return a successful result.
if (buffer && !length)
return 0;
DWORD result = ::GetFontData(hdc, table, offset, buffer, length);
if (result == GDI_ERROR) {
// The font may not be cached by the OS, causing an attempt to read it in
// the renderer process to fail. Attempt to pre-cache it.
LOGFONTW logfont;
if (!::GetObject(font_, sizeof(LOGFONTW), &logfont))
return GDI_ERROR;
RenderThread* render_thread = RenderThread::Get();
if (!render_thread)
return GDI_ERROR;
render_thread->PreCacheFont(logfont);
result = ::GetFontData(hdc, table, offset, buffer, length);
}
return result;
}
int32_t PepperTrueTypeFontWin::GetTableTags(std::vector<uint32_t>* tags) {
base::win::ScopedCreateDC hdc(::CreateCompatibleDC(NULL));
if (!hdc)
return PP_ERROR_FAILED;
base::win::ScopedSelectObject select_object(hdc, font_);
// Get the whole font header.
static const DWORD kFontHeaderSize = 12;
uint8_t header_buf[kFontHeaderSize];
if (GetFontData(hdc, 0, 0, header_buf, kFontHeaderSize) == GDI_ERROR)
return PP_ERROR_FAILED;
// The numTables follows a 4 byte scalerType tag. Font data is stored in
// big-endian order.
DWORD num_tables = (header_buf[4] << 8) | header_buf[5];
// The size in bytes of an entry in the table directory.
static const DWORD kDirectoryEntrySize = 16;
DWORD directory_size = num_tables * kDirectoryEntrySize;
scoped_ptr<uint8_t[]> directory(new uint8_t[directory_size]);
// Get the table directory entries after the font header.
if (GetFontData(
hdc, 0 /* tag */, kFontHeaderSize, directory.get(), directory_size) ==
GDI_ERROR)
return PP_ERROR_FAILED;
tags->resize(num_tables);
for (DWORD i = 0; i < num_tables; i++) {
const uint8_t* entry = directory.get() + i * kDirectoryEntrySize;
uint32_t tag = static_cast<uint32_t>(entry[0]) << 24 |
static_cast<uint32_t>(entry[1]) << 16 |
static_cast<uint32_t>(entry[2]) << 8 |
static_cast<uint32_t>(entry[3]);
(*tags)[i] = tag;
}
return num_tables;
}
int32_t PepperTrueTypeFontWin::GetTable(uint32_t table_tag,
int32_t offset,
int32_t max_data_length,
std::string* data) {
base::win::ScopedCreateDC hdc(::CreateCompatibleDC(NULL));
if (!hdc)
return PP_ERROR_FAILED;
base::win::ScopedSelectObject select_object(hdc, font_);
// Tags are byte swapped on Windows.
table_tag = base::ByteSwap(table_tag);
// Get the size of the font table first.
DWORD table_size = GetFontData(hdc, table_tag, 0, NULL, 0);
if (table_size == GDI_ERROR)
return PP_ERROR_FAILED;
DWORD safe_offset = std::min(static_cast<DWORD>(offset), table_size);
DWORD safe_length =
std::min(table_size - safe_offset, static_cast<DWORD>(max_data_length));
data->resize(safe_length);
if (safe_length == 0) {
table_size = 0;
} else {
table_size = GetFontData(hdc,
table_tag,
safe_offset,
reinterpret_cast<uint8_t*>(&(*data)[0]),
safe_length);
if (table_size == GDI_ERROR)
return PP_ERROR_FAILED;
}
return static_cast<int32_t>(table_size);
}
} // namespace
// static
PepperTrueTypeFont* PepperTrueTypeFont::Create(
const ppapi::proxy::SerializedTrueTypeFontDesc& desc) {
return new PepperTrueTypeFontWin(desc);
}
} // namespace content