OpenTypeVerticalData.cpp   [plain text]


/*
 * Copyright (C) 2012 Koji Ishii <kojiishi@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "OpenTypeVerticalData.h"

#include "FloatRect.h"
#include "GlyphPage.h"
#include "OpenTypeTypes.h"
#include "SharedBuffer.h"
#include "SimpleFontData.h"
#include <wtf/RefPtr.h>

using namespace std;

namespace WebCore {
namespace OpenType {

enum {
    HheaTag = OT_MAKE_TAG('h', 'h', 'e', 'a'),
    HmtxTag = OT_MAKE_TAG('h', 'm', 't', 'x'),
    VheaTag = OT_MAKE_TAG('v', 'h', 'e', 'a'),
    VmtxTag = OT_MAKE_TAG('v', 'm', 't', 'x'),
    VORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G'),
};

#pragma pack(1)

struct HheaTable {
    OpenType::Fixed version;
    OpenType::Int16 ascender;
    OpenType::Int16 descender;
    OpenType::Int16 lineGap;
    OpenType::Int16 advanceWidthMax;
    OpenType::Int16 minLeftSideBearing;
    OpenType::Int16 minRightSideBearing;
    OpenType::Int16 xMaxExtent;
    OpenType::Int16 caretSlopeRise;
    OpenType::Int16 caretSlopeRun;
    OpenType::Int16 caretOffset;
    OpenType::Int16 reserved[4];
    OpenType::Int16 metricDataFormat;
    OpenType::UInt16 numberOfHMetrics;
};

struct VheaTable {
    OpenType::Fixed version;
    OpenType::Int16 ascent;
    OpenType::Int16 descent;
    OpenType::Int16 lineGap;
    OpenType::Int16 advanceHeightMax;
    OpenType::Int16 minTopSideBearing;
    OpenType::Int16 minBottomSideBearing;
    OpenType::Int16 yMaxExtent;
    OpenType::Int16 caretSlopeRise;
    OpenType::Int16 caretSlopeRun;
    OpenType::Int16 caretOffset;
    OpenType::Int16 reserved[4];
    OpenType::Int16 metricDataFormat;
    OpenType::UInt16 numOfLongVerMetrics;
};

struct HmtxTable {
    struct Entry {
        OpenType::UInt16 advanceWidth;
        OpenType::Int16 lsb;
    } entries[1];
};

struct VmtxTable {
    struct Entry {
        OpenType::UInt16 advanceHeight;
        OpenType::Int16 topSideBearing;
    } entries[1];
};

struct VORGTable {
    OpenType::UInt16 majorVersion;
    OpenType::UInt16 minorVersion;
    OpenType::Int16 defaultVertOriginY;
    OpenType::UInt16 numVertOriginYMetrics;
    struct VertOriginYMetrics {
        OpenType::UInt16 glyphIndex;
        OpenType::Int16 vertOriginY;
    } vertOriginYMetrics[1];

    size_t requiredSize() const { return sizeof(*this) + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); }
};

#pragma pack()

} // namespace OpenType

template <typename T> const T* validatedPtr(const RefPtr<SharedBuffer>& buffer, size_t count = 1)
{
    if (!buffer || buffer->size() < sizeof(T) * count)
        return 0;
    return reinterpret_cast<const T*>(buffer->data());
}

OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData)
    : m_defaultVertOriginY(0)
{
    // Load hhea and hmtx to get x-component of vertical origins.
    // If these tables are missing, it's not an OpenType font.
    RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::HheaTag);
    const OpenType::HheaTable* hhea = validatedPtr<OpenType::HheaTable>(buffer);
    if (!hhea)
        return;
    uint16_t countHmtxEntries = hhea->numberOfHMetrics;
    if (!countHmtxEntries) {
        LOG_ERROR("Invalid numberOfHMetrics");
        return;
    }

    buffer = platformData.openTypeTable(OpenType::HmtxTag);
    const OpenType::HmtxTable* hmtx = validatedPtr<OpenType::HmtxTable>(buffer, countHmtxEntries);
    if (!hmtx) {
        LOG_ERROR("hhea exists but hmtx does not (or broken)");
        return;
    }
    m_advanceWidths.resize(countHmtxEntries);
    for (uint16_t i = 0; i < countHmtxEntries; ++i)
        m_advanceWidths[i] = hmtx->entries[i].advanceWidth;

    // Load vhea first. This table is required for fonts that support vertical flow.
    buffer = platformData.openTypeTable(OpenType::VheaTag);
    const OpenType::VheaTable* vhea = validatedPtr<OpenType::VheaTable>(buffer);
    if (!vhea)
        return;
    uint16_t countVmtxEntries = vhea->numOfLongVerMetrics;
    if (!countVmtxEntries) {
        LOG_ERROR("Invalid numOfLongVerMetrics");
        return;
    }

    // Load VORG. This table is optional.
    buffer = platformData.openTypeTable(OpenType::VORGTag);
    const OpenType::VORGTable* vorg = validatedPtr<OpenType::VORGTable>(buffer);
    if (vorg && buffer->size() >= vorg->requiredSize()) {
        m_defaultVertOriginY = vorg->defaultVertOriginY;
        uint16_t countVertOriginYMetrics = vorg->numVertOriginYMetrics;
        if (!countVertOriginYMetrics) {
            // Add one entry so that hasVORG() becomes true
            m_vertOriginY.set(0, m_defaultVertOriginY);
        } else {
            for (uint16_t i = 0; i < countVertOriginYMetrics; ++i) {
                const OpenType::VORGTable::VertOriginYMetrics& metrics = vorg->vertOriginYMetrics[i];
                m_vertOriginY.set(metrics.glyphIndex, metrics.vertOriginY);
            }
        }
    }

    // Load vmtx then. This table is required for fonts that support vertical flow.
    buffer = platformData.openTypeTable(OpenType::VmtxTag);
    const OpenType::VmtxTable* vmtx = validatedPtr<OpenType::VmtxTable>(buffer, countVmtxEntries);
    if (!vmtx) {
        LOG_ERROR("vhea exists but vmtx does not (or broken)");
        return;
    }
    m_advanceHeights.resize(countVmtxEntries);
    for (uint16_t i = 0; i < countVmtxEntries; ++i)
        m_advanceHeights[i] = vmtx->entries[i].advanceHeight;

    // VORG is preferred way to calculate vertical origin than vmtx,
    // so load topSideBearing from vmtx only if VORG is missing.
    if (hasVORG())
        return;

    size_t sizeExtra = buffer->size() - sizeof(OpenType::VmtxTable::Entry) * countVmtxEntries;
    if (sizeExtra % sizeof(OpenType::Int16)) {
        LOG_ERROR("vmtx has incorrect tsb count");
        return;
    }
    size_t countTopSideBearings = countVmtxEntries + sizeExtra / sizeof(OpenType::Int16);
    m_topSideBearings.resize(countTopSideBearings);
    size_t i;
    for (i = 0; i < countVmtxEntries; ++i)
        m_topSideBearings[i] = vmtx->entries[i].topSideBearing;
    if (i < countTopSideBearings) {
        const OpenType::Int16* pTopSideBearingsExtra = reinterpret_cast<const OpenType::Int16*>(&vmtx->entries[countVmtxEntries]);
        for (; i < countTopSideBearings; ++i, ++pTopSideBearingsExtra)
            m_topSideBearings[i] = *pTopSideBearingsExtra;
    }
}

float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyph) const
{
    size_t countHeights = m_advanceHeights.size();
    if (countHeights) {
        uint16_t advanceFUnit = m_advanceHeights[glyph < countHeights ? glyph : countHeights - 1];
        float advance = advanceFUnit * font->sizePerUnit();
        return advance;
    }

    // No vertical info in the font file; use height as advance.
    return font->fontMetrics().height();
}

void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData* font, const Glyph* glyphs, size_t count, float* outXYArray) const
{
    size_t countWidths = m_advanceWidths.size();
    ASSERT(countWidths > 0);
    const FontMetrics& metrics = font->fontMetrics();
    float sizePerUnit = font->sizePerUnit();
    float ascent = metrics.ascent();
    bool useVORG = hasVORG();
    size_t countTopSideBearings = m_topSideBearings.size();
    float defaultVertOriginY = std::numeric_limits<float>::quiet_NaN();
    for (float* end = &(outXYArray[count * 2]); outXYArray != end; ++glyphs, outXYArray += 2) {
        Glyph glyph = *glyphs;
        uint16_t widthFUnit = m_advanceWidths[glyph < countWidths ? glyph : countWidths - 1];
        float width = widthFUnit * sizePerUnit;
        outXYArray[0] = -width / 2;

        // For Y, try VORG first.
        if (useVORG) {
            int16_t vertOriginYFUnit = m_vertOriginY.get(glyph);
            if (vertOriginYFUnit) {
                outXYArray[1] = -vertOriginYFUnit * sizePerUnit;
                continue;
            }
            if (isnan(defaultVertOriginY))
                defaultVertOriginY = -m_defaultVertOriginY * sizePerUnit;
            outXYArray[1] = defaultVertOriginY;
            continue;
        }

        // If no VORG, try vmtx next.
        if (countTopSideBearings) {
            int16_t topSideBearingFUnit = m_topSideBearings[glyph < countTopSideBearings ? glyph : countTopSideBearings - 1];
            float topSideBearing = topSideBearingFUnit * sizePerUnit;
            FloatRect bounds = font->boundsForGlyph(glyph);
            outXYArray[1] = bounds.y() - topSideBearing;
            continue;
        }

        // No vertical info in the font file; use ascent as vertical origin.
        outXYArray[1] = -ascent;
    }
}

} // namespace WebCore