Bug 1464094 - print font variations as paths for PDF/PS output. r=jfkthame

MozReview-Commit-ID: 3sPKD4pNwdy
This commit is contained in:
Lee Salzman 2018-06-01 13:08:57 -04:00
Родитель 67537a721b
Коммит 50e6372cf9
10 изменённых файлов: 253 добавлений и 22 удалений

Просмотреть файл

@ -881,6 +881,8 @@ public:
virtual bool CanSerialize() { return false; }
virtual bool HasVariationSettings() { return false; }
void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
mUserData.Add(key, userData, destroy);
}

Просмотреть файл

@ -15,6 +15,7 @@
#include "mozilla/Scoped.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
#include "gfxPrefs.h"
#include "cairo.h"
#include "cairo-tee.h"
@ -1348,6 +1349,19 @@ DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA)
#endif
}
static bool
SupportsVariationSettings(cairo_surface_t* surface)
{
switch (cairo_surface_get_type(surface))
{
case CAIRO_SURFACE_TYPE_PDF:
case CAIRO_SURFACE_TYPE_PS:
return false;
default:
return true;
}
}
void
DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
@ -1403,7 +1417,17 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
}
cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
if (!SupportsVariationSettings(mSurface) &&
aFont->HasVariationSettings() &&
gfxPrefs::PrintFontVariationsAsPaths()) {
cairo_set_fill_rule(mContext, CAIRO_FILL_RULE_WINDING);
cairo_new_path(mContext);
cairo_glyph_path(mContext, &glyphs[0], aBuffer.mNumGlyphs);
cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
cairo_fill(mContext);
} else {
cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
}
if (cairo_surface_status(cairo_get_group_target(mContext))) {
gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(cairo_get_group_target(mContext));

Просмотреть файл

@ -12,9 +12,12 @@
namespace mozilla {
namespace gfx {
NativeFontResourceFontconfig::NativeFontResourceFontconfig(UniquePtr<uint8_t[]>&& aFontData, FT_Face aFace)
: mFontData(std::move(aFontData)),
mFace(aFace)
NativeFontResourceFontconfig::NativeFontResourceFontconfig(UniquePtr<uint8_t[]>&& aFontData,
uint32_t aDataLength,
FT_Face aFace)
: mFontData(std::move(aFontData))
, mDataLength(aDataLength)
, mFace(aFace)
{
}
@ -48,7 +51,7 @@ NativeFontResourceFontconfig::Create(uint8_t *aFontData, uint32_t aDataLength, F
}
RefPtr<NativeFontResourceFontconfig> resource =
new NativeFontResourceFontconfig(std::move(fontData), face);
new NativeFontResourceFontconfig(std::move(fontData), aDataLength, face);
return resource.forget();
}
@ -60,5 +63,11 @@ NativeFontResourceFontconfig::CreateUnscaledFont(uint32_t aIndex,
return unscaledFont.forget();
}
FT_Face
NativeFontResourceFontconfig::CloneFace()
{
return Factory::NewFTFaceFromData(mFace->glyph->library, mFontData.get(), mDataLength, 0);
}
} // gfx
} // mozilla

Просмотреть файл

@ -29,10 +29,15 @@ public:
~NativeFontResourceFontconfig();
FT_Face CloneFace();
private:
NativeFontResourceFontconfig(UniquePtr<uint8_t[]>&& aFontData, FT_Face aFace);
NativeFontResourceFontconfig(UniquePtr<uint8_t[]>&& aFontData,
uint32_t aDataLength,
FT_Face aFace);
UniquePtr<uint8_t[]> mFontData;
uint32_t mDataLength;
FT_Face mFace;
};

Просмотреть файл

@ -6,7 +6,9 @@
#include "ScaledFontFontconfig.h"
#include "UnscaledFontFreeType.h"
#include "NativeFontResourceFontconfig.h"
#include "Logging.h"
#include "StackArray.h"
#include "mozilla/webrender/WebRenderTypes.h"
#ifdef USE_SKIA
@ -14,6 +16,9 @@
#endif
#include <fontconfig/fcfreetype.h>
#include <dlfcn.h>
#include FT_MULTIPLE_MASTERS_H
namespace mozilla {
namespace gfx {
@ -224,12 +229,67 @@ ScaledFontFontconfig::InstanceData::SetupFontMatrix(cairo_matrix_t* aFontMatrix)
cairo_matrix_init(aFontMatrix, mScale, 0, mSkew, mScale, 0, 0);
}
void
ScaledFontFontconfig::GetVariationSettings(std::vector<FontVariation>* aVariations)
{
aVariations->clear();
typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
typedef FT_Error (*GetVarDesignCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
static GetVarFunc getVar;
static DoneVarFunc doneVar;
static GetVarDesignCoordsFunc getCoords;
static bool firstTime = true;
if (firstTime) {
firstTime = false;
getVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
doneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
getCoords = (GetVarDesignCoordsFunc)dlsym(RTLD_DEFAULT, "FT_Get_Var_Design_Coordinates");
}
cairo_scaled_font_t* sf = GetCairoScaledFont();
FT_Face face = cairo_ft_scaled_font_lock_face(sf);
if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
FT_MM_Var* mmVar;
if ((*getVar)(face, &mmVar) == FT_Err_Ok) {
aVariations->reserve(mmVar->num_axis);
StackArray<FT_Fixed, 32> coords(mmVar->num_axis);
if ((*getCoords)(face, mmVar->num_axis, coords.data()) == FT_Err_Ok) {
bool changed = false;
for (uint32_t i = 0; i < mmVar->num_axis; i++) {
if (coords[i] != mmVar->axis[i].def) {
changed = true;
}
aVariations->push_back(FontVariation{uint32_t(mmVar->axis[i].tag),
float(coords[i] / 65536.0)});
}
if (!changed) {
aVariations->clear();
}
}
if (doneVar) {
(*doneVar)(face->glyph->library, mmVar);
} else {
free(mmVar);
}
}
}
cairo_ft_scaled_font_unlock_face(sf);
}
bool
ScaledFontFontconfig::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton)
{
InstanceData instance(GetCairoScaledFont(), mPattern);
aCb(reinterpret_cast<uint8_t*>(&instance), sizeof(instance), nullptr, 0, aBaton);
std::vector<FontVariation> variations;
if (HasVariationSettings()) {
GetVariationSettings(&variations);
}
aCb(reinterpret_cast<uint8_t*>(&instance), sizeof(instance),
variations.data(), variations.size(), aBaton);
return true;
}
@ -348,6 +408,11 @@ ScaledFontFontconfig::GetWRFontInstanceOptions(Maybe<wr::FontInstanceOptions>* a
*aOutOptions = Some(options);
*aOutPlatformOptions = Some(platformOptions);
if (HasVariationSettings()) {
GetVariationSettings(aOutVariations);
}
return true;
}
@ -364,8 +429,12 @@ UnscaledFontFontconfig::CreateScaledFont(Float aGlyphSize,
}
const ScaledFontFontconfig::InstanceData *instanceData =
reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(aInstanceData);
return ScaledFontFontconfig::CreateFromInstanceData(*instanceData, this, aGlyphSize,
mNativeFontResource.get());
RefPtr<ScaledFont> scaledFont =
ScaledFontFontconfig::CreateFromInstanceData(*instanceData, this, aGlyphSize,
aVariations, aNumVariations,
static_cast<NativeFontResourceFontconfig*>(
mNativeFontResource.get()));
return scaledFont.forget();
}
static cairo_user_data_key_t sNativeFontResourceKey;
@ -376,20 +445,37 @@ ReleaseNativeFontResource(void* aData)
static_cast<NativeFontResource*>(aData)->Release();
}
static cairo_user_data_key_t sFaceKey;
static void
ReleaseFace(void* aData)
{
Factory::ReleaseFTFace(static_cast<FT_Face>(aData));
}
already_AddRefed<ScaledFont>
ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
UnscaledFontFontconfig* aUnscaledFont,
Float aSize,
NativeFontResource* aNativeFontResource)
const FontVariation* aVariations,
uint32_t aNumVariations,
NativeFontResourceFontconfig* aNativeFontResource)
{
FcPattern* pattern = FcPatternCreate();
if (!pattern) {
gfxWarning() << "Failing initializing Fontconfig pattern for scaled font";
gfxWarning() << "Failed initializing Fontconfig pattern for scaled font";
return nullptr;
}
FT_Face face = aUnscaledFont->GetFace();
FT_Face varFace = nullptr;
if (face) {
FcPatternAddFTFace(pattern, FC_FT_FACE, face);
if (aNativeFontResource && aNumVariations > 0) {
varFace = aNativeFontResource->CloneFace();
if (!varFace) {
gfxWarning() << "Failed cloning face for variations";
}
}
FcPatternAddFTFace(pattern, FC_FT_FACE, varFace ? varFace : face);
} else {
FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(aUnscaledFont->GetFile()));
FcPatternAddInteger(pattern, FC_INDEX, aUnscaledFont->GetIndex());
@ -397,10 +483,18 @@ ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aSize);
aInstanceData.SetupPattern(pattern);
cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern, nullptr, 0);
StackArray<FT_Fixed, 32> coords(aNumVariations);
for (uint32_t i = 0; i < aNumVariations; i++) {
coords[i] = std::round(aVariations[i].mValue * 65536.0);
}
cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern, coords.data(), aNumVariations);
if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
gfxWarning() << "Failed creating Cairo font face for Fontconfig pattern";
FcPatternDestroy(pattern);
if (varFace) {
Factory::ReleaseFTFace(varFace);
}
return nullptr;
}
@ -414,13 +508,28 @@ ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
// to prevent races if multiple threads are thus sharing the same Cairo face.
FT_Library library = face ? face->glyph->library : Factory::GetFTLibrary();
Factory::LockFTLibrary(library);
cairo_status_t err = cairo_font_face_set_user_data(font,
&sNativeFontResourceKey,
aNativeFontResource,
ReleaseNativeFontResource);
cairo_status_t err = CAIRO_STATUS_SUCCESS;
bool cleanupFace = false;
if (varFace) {
err = cairo_font_face_set_user_data(font,
&sFaceKey,
varFace,
ReleaseFace);
}
if (err != CAIRO_STATUS_SUCCESS) {
cleanupFace = true;
} else {
err = cairo_font_face_set_user_data(font,
&sNativeFontResourceKey,
aNativeFontResource,
ReleaseNativeFontResource);
}
Factory::UnlockFTLibrary(library);
if (err != CAIRO_STATUS_SUCCESS) {
gfxWarning() << "Failed binding NativeFontResource to Cairo font face";
if (varFace && cleanupFace) {
Factory::ReleaseFTFace(varFace);
}
aNativeFontResource->Release();
cairo_font_face_destroy(font);
FcPatternDestroy(pattern);
@ -455,9 +564,51 @@ ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
cairo_scaled_font_destroy(cairoScaledFont);
FcPatternDestroy(pattern);
// Only apply variations if we have an explicitly cloned face. Otherwise,
// if the pattern holds the pathname, Cairo will handle setting of variations.
if (varFace) {
scaledFont->ApplyVariations(aVariations, aNumVariations);
}
return scaledFont.forget();
}
void
ScaledFontFontconfig::ApplyVariations(const FontVariation* aVariations,
uint32_t aNumVariations)
{
typedef FT_Error (*SetVarDesignCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
static SetVarDesignCoordsFunc setCoords;
static bool firstTime = true;
if (firstTime) {
firstTime = false;
setCoords = (SetVarDesignCoordsFunc)dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
}
cairo_scaled_font_t* sf = GetCairoScaledFont();
FT_Face face = cairo_ft_scaled_font_lock_face(sf);
if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
StackArray<FT_Fixed, 32> coords(aNumVariations);
for (uint32_t i = 0; i < aNumVariations; i++) {
coords[i] = std::round(aVariations[i].mValue * 65536.0f);
}
if ((*setCoords)(face, aNumVariations, coords.data()) != FT_Err_Ok) {
// ignore the problem?
}
}
cairo_ft_scaled_font_unlock_face(sf);
}
bool
ScaledFontFontconfig::HasVariationSettings()
{
// Check if the FT face has been cloned.
FT_Face face = nullptr;
return FcPatternGetFTFace(mPattern, FC_FT_FACE, 0, &face) == FcResultMatch &&
face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS &&
face != static_cast<UnscaledFontFontconfig*>(mUnscaledFont.get())->GetFace();
}
already_AddRefed<UnscaledFont>
UnscaledFontFontconfig::CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex)
{

Просмотреть файл

@ -39,10 +39,16 @@ public:
Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
std::vector<FontVariation>* aOutVariations) override;
void ApplyVariations(const FontVariation* aVariations, uint32_t aNumVariations);
bool HasVariationSettings() override;
private:
friend class NativeFontResourceFontconfig;
friend class UnscaledFontFontconfig;
void GetVariationSettings(std::vector<FontVariation>* aVariations);
struct InstanceData
{
enum {
@ -72,7 +78,9 @@ private:
CreateFromInstanceData(const InstanceData& aInstanceData,
UnscaledFontFontconfig* aUnscaledFont,
Float aSize,
NativeFontResource* aNativeFontResource = nullptr);
const FontVariation* aVariations,
uint32_t aNumVariations,
NativeFontResourceFontconfig* aNativeFontResource = nullptr);
FcPattern* mPattern;
};

Просмотреть файл

@ -749,10 +749,19 @@ ReleaseFTUserFontData(void* aData)
static_cast<FTUserFontData*>(aData)->Release();
}
static cairo_user_data_key_t sFcFontlistFTFaceKey;
static void
ReleaseFTFace(void* aData)
{
Factory::ReleaseFTFace(static_cast<FT_Face>(aData));
}
cairo_scaled_font_t*
gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
gfxFloat aAdjustedSize,
const gfxFontStyle *aStyle)
const gfxFontStyle *aStyle,
FT_Face aFTFace)
{
if (aStyle->NeedsSyntheticBold(this)) {
FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
@ -784,9 +793,21 @@ gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
coords.Elements(),
coords.Length());
if (aFTFace) {
if (cairo_font_face_set_user_data(face,
&sFcFontlistFTFaceKey,
aFTFace,
ReleaseFTFace) != CAIRO_STATUS_SUCCESS) {
NS_WARNING("Failed binding FT_Face to Cairo font face");
cairo_font_face_destroy(face);
Factory::ReleaseFTFace(aFTFace);
return nullptr;
}
}
if (mFontData) {
// for data fonts, add the face/data pointer to the cairo font face
// so that it gets deleted whenever cairo decides
// so that it ges deleted whenever cairo decides
NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
mUserFontData.get()->AddRef();
@ -1009,11 +1030,14 @@ gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle)
}
if (!renderPattern) {
NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
if (face != mFTFace) {
Factory::ReleaseFTFace(face);
}
return nullptr;
}
cairo_scaled_font_t* scaledFont =
CreateScaledFont(renderPattern, size, aFontStyle);
CreateScaledFont(renderPattern, size, aFontStyle, face != mFTFace ? face : nullptr);
const FcChar8* file = ToFcChar8Ptr("");
int index = 0;

Просмотреть файл

@ -139,7 +139,8 @@ protected:
cairo_scaled_font_t*
CreateScaledFont(FcPattern* aRenderPattern,
gfxFloat aAdjustedSize,
const gfxFontStyle *aStyle);
const gfxFontStyle *aStyle,
FT_Face aFTFace);
// override to pull data from FTFace
virtual nsresult

Просмотреть файл

@ -725,6 +725,8 @@ private:
DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
DECL_GFX_PREF(Live, "print.font-variations-as-paths", PrintFontVariationsAsPaths, bool, true);
DECL_GFX_PREF(Once, "slider.snapMultiplier", SliderSnapMultiplier, int32_t, 0);
DECL_GFX_PREF(Live, "test.events.async.enabled", TestEventsAsyncEnabled, bool, false);

Просмотреть файл

@ -1138,6 +1138,11 @@ pref("print.print_via_parent", true);
pref("print.print_via_parent", false);
#endif
// Variation fonts can't always be embedded in certain output formats
// such as PDF. To work around this, draw the variation fonts using
// paths instead of using font embedding.
pref("print.font-variations-as-paths", true);
// Pref used by the spellchecker extension to control the
// maximum number of misspelled words that will be underlined
// in a document.