| /* |
| * 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 "platform/fonts/opentype/OpenTypeVerticalData.h" |
| |
| #include "SkTypeface.h" |
| #include "platform/SharedBuffer.h" |
| #include "platform/fonts/SimpleFontData.h" |
| #include "platform/fonts/GlyphPage.h" |
| #include "platform/fonts/opentype/OpenTypeTypes.h" |
| #include "platform/geometry/FloatRect.h" |
| #include "wtf/RefPtr.h" |
| |
| namespace blink { |
| namespace OpenType { |
| |
| // The input characters are big-endian (first is most significant). |
| #define OT_MAKE_TAG(ch1, ch2, ch3, ch4) \ |
| ((((uint32_t)(ch1)) << 24) | (((uint32_t)(ch2)) << 16) | \ |
| (((uint32_t)(ch3)) << 8) | ((uint32_t)(ch4))) |
| |
| const SkFontTableTag HheaTag = OT_MAKE_TAG('h', 'h', 'e', 'a'); |
| const SkFontTableTag HmtxTag = OT_MAKE_TAG('h', 'm', 't', 'x'); |
| const SkFontTableTag VheaTag = OT_MAKE_TAG('v', 'h', 'e', 'a'); |
| const SkFontTableTag VmtxTag = OT_MAKE_TAG('v', 'm', 't', 'x'); |
| const SkFontTableTag VORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G'); |
| |
| #pragma pack(1) |
| |
| struct HheaTable { |
| DISALLOW_NEW(); |
| 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 { |
| DISALLOW_NEW(); |
| 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 { |
| DISALLOW_NEW(); |
| struct Entry { |
| DISALLOW_NEW(); |
| OpenType::UInt16 advanceWidth; |
| OpenType::Int16 lsb; |
| } entries[1]; |
| }; |
| |
| struct VmtxTable { |
| DISALLOW_NEW(); |
| struct Entry { |
| DISALLOW_NEW(); |
| OpenType::UInt16 advanceHeight; |
| OpenType::Int16 topSideBearing; |
| } entries[1]; |
| }; |
| |
| struct VORGTable { |
| DISALLOW_NEW(); |
| OpenType::UInt16 majorVersion; |
| OpenType::UInt16 minorVersion; |
| OpenType::Int16 defaultVertOriginY; |
| OpenType::UInt16 numVertOriginYMetrics; |
| struct VertOriginYMetrics { |
| DISALLOW_NEW(); |
| OpenType::UInt16 glyphIndex; |
| OpenType::Int16 vertOriginY; |
| } vertOriginYMetrics[1]; |
| |
| size_t requiredSize() const { |
| return sizeof(*this) + |
| sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); |
| } |
| }; |
| |
| #pragma pack() |
| |
| } // namespace OpenType |
| |
| OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData) |
| : m_defaultVertOriginY(0) { |
| loadMetrics(platformData); |
| } |
| |
| void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData) { |
| // 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 = |
| OpenType::validateTable<OpenType::HheaTable>(buffer); |
| if (!hhea) |
| return; |
| uint16_t countHmtxEntries = hhea->numberOfHMetrics; |
| if (!countHmtxEntries) { |
| DLOG(ERROR) << "Invalid numberOfHMetrics"; |
| return; |
| } |
| |
| buffer = platformData.openTypeTable(OpenType::HmtxTag); |
| const OpenType::HmtxTable* hmtx = |
| OpenType::validateTable<OpenType::HmtxTable>(buffer, countHmtxEntries); |
| if (!hmtx) { |
| DLOG(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 = |
| OpenType::validateTable<OpenType::VheaTable>(buffer); |
| if (!vhea) |
| return; |
| uint16_t countVmtxEntries = vhea->numOfLongVerMetrics; |
| if (!countVmtxEntries) { |
| DLOG(ERROR) << "Invalid numOfLongVerMetrics"; |
| return; |
| } |
| |
| // Load VORG. This table is optional. |
| buffer = platformData.openTypeTable(OpenType::VORGTag); |
| const OpenType::VORGTable* vorg = |
| OpenType::validateTable<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 = |
| OpenType::validateTable<OpenType::VmtxTable>(buffer, countVmtxEntries); |
| if (!vmtx) { |
| DLOG(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)) { |
| DLOG(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->getFontMetrics().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->getFontMetrics(); |
| 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) { |
| if (glyph) { |
| int16_t vertOriginYFUnit = m_vertOriginY.get(glyph); |
| if (vertOriginYFUnit) { |
| outXYArray[1] = -vertOriginYFUnit * sizePerUnit; |
| continue; |
| } |
| } |
| if (std::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 blink |