| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| * OWNER OR 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/WebFontDecoder.h" |
| |
| #include "platform/Histogram.h" |
| #include "platform/SharedBuffer.h" |
| #include "platform/fonts/FontCache.h" |
| #include "platform/graphics/skia/SkiaUtils.h" |
| #include "platform/instrumentation/tracing/TraceEvent.h" |
| #include "platform/wtf/CurrentTime.h" |
| #include "public/platform/Platform.h" |
| #include "third_party/harfbuzz-ng/src/hb.h" |
| #include "third_party/ots/include/ots-memory-stream.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| |
| #include <stdarg.h> |
| |
| namespace blink { |
| |
| namespace { |
| |
| class BlinkOTSContext final : public ots::OTSContext { |
| DISALLOW_NEW(); |
| |
| public: |
| void Message(int level, const char* format, ...) override; |
| ots::TableAction GetTableAction(uint32_t tag) override; |
| const String& GetErrorString() { return error_string_; } |
| |
| private: |
| String error_string_; |
| }; |
| |
| void BlinkOTSContext::Message(int level, const char* format, ...) { |
| va_list args; |
| va_start(args, format); |
| |
| #if COMPILER(MSVC) |
| int result = _vscprintf(format, args); |
| #else |
| char ch; |
| int result = vsnprintf(&ch, 1, format, args); |
| #endif |
| va_end(args); |
| |
| if (result <= 0) { |
| error_string_ = String("OTS Error"); |
| } else { |
| Vector<char, 256> buffer; |
| unsigned len = result; |
| buffer.Grow(len + 1); |
| |
| va_start(args, format); |
| vsnprintf(buffer.Data(), buffer.size(), format, args); |
| va_end(args); |
| error_string_ = |
| StringImpl::Create(reinterpret_cast<const LChar*>(buffer.Data()), len); |
| } |
| } |
| |
| #if !defined(HB_VERSION_ATLEAST) |
| #define HB_VERSION_ATLEAST(major, minor, micro) 0 |
| #endif |
| |
| ots::TableAction BlinkOTSContext::GetTableAction(uint32_t tag) { |
| const uint32_t kCbdtTag = OTS_TAG('C', 'B', 'D', 'T'); |
| const uint32_t kCblcTag = OTS_TAG('C', 'B', 'L', 'C'); |
| const uint32_t kColrTag = OTS_TAG('C', 'O', 'L', 'R'); |
| const uint32_t kCpalTag = OTS_TAG('C', 'P', 'A', 'L'); |
| #if HB_VERSION_ATLEAST(1, 0, 0) |
| const uint32_t kGdefTag = OTS_TAG('G', 'D', 'E', 'F'); |
| const uint32_t kGposTag = OTS_TAG('G', 'P', 'O', 'S'); |
| const uint32_t kGsubTag = OTS_TAG('G', 'S', 'U', 'B'); |
| |
| // Font Variations related tables |
| // See "Variation Tables" in Terminology section of |
| // https://www.microsoft.com/typography/otspec/otvaroverview.htm |
| const uint32_t kAvarTag = OTS_TAG('a', 'v', 'a', 'r'); |
| const uint32_t kCvarTag = OTS_TAG('c', 'v', 'a', 'r'); |
| const uint32_t kFvarTag = OTS_TAG('f', 'v', 'a', 'r'); |
| const uint32_t kGvarTag = OTS_TAG('g', 'v', 'a', 'r'); |
| const uint32_t kHvarTag = OTS_TAG('H', 'V', 'A', 'R'); |
| const uint32_t kMvarTag = OTS_TAG('M', 'V', 'A', 'R'); |
| const uint32_t kVvarTag = OTS_TAG('V', 'V', 'A', 'R'); |
| #endif |
| |
| switch (tag) { |
| // Google Color Emoji Tables |
| case kCbdtTag: |
| case kCblcTag: |
| // Windows Color Emoji Tables |
| case kColrTag: |
| case kCpalTag: |
| #if HB_VERSION_ATLEAST(1, 0, 0) |
| // Let HarfBuzz handle how to deal with broken tables. |
| case kAvarTag: |
| case kCvarTag: |
| case kFvarTag: |
| case kGvarTag: |
| case kHvarTag: |
| case kMvarTag: |
| case kVvarTag: |
| case kGdefTag: |
| case kGposTag: |
| case kGsubTag: |
| #endif |
| return ots::TABLE_ACTION_PASSTHRU; |
| default: |
| return ots::TABLE_ACTION_DEFAULT; |
| } |
| } |
| |
| void RecordDecodeSpeedHistogram(const char* data, |
| size_t length, |
| double decode_time, |
| size_t decoded_size) { |
| if (decode_time <= 0) |
| return; |
| |
| double kb_per_second = decoded_size / (1000 * decode_time); |
| if (length >= 4) { |
| if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { |
| DEFINE_THREAD_SAFE_STATIC_LOCAL( |
| CustomCountHistogram, woff_histogram, |
| new CustomCountHistogram("WebFont.DecodeSpeed.WOFF", 1000, 300000, |
| 50)); |
| woff_histogram.Count(kb_per_second); |
| return; |
| } |
| |
| if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') { |
| DEFINE_THREAD_SAFE_STATIC_LOCAL( |
| CustomCountHistogram, woff2_histogram, |
| new CustomCountHistogram("WebFont.DecodeSpeed.WOFF2", 1000, 300000, |
| 50)); |
| woff2_histogram.Count(kb_per_second); |
| return; |
| } |
| } |
| |
| DEFINE_THREAD_SAFE_STATIC_LOCAL( |
| CustomCountHistogram, sfnt_histogram, |
| new CustomCountHistogram("WebFont.DecodeSpeed.SFNT", 1000, 300000, 50)); |
| sfnt_histogram.Count(kb_per_second); |
| } |
| |
| } // namespace |
| |
| // static |
| bool WebFontDecoder::SupportsFormat(const String& format) { |
| return DeprecatedEqualIgnoringCase(format, "woff") || |
| DeprecatedEqualIgnoringCase(format, "woff2"); |
| } |
| |
| sk_sp<SkTypeface> WebFontDecoder::Decode(SharedBuffer* buffer) { |
| if (!buffer) { |
| SetErrorString("Empty Buffer"); |
| return nullptr; |
| } |
| |
| // This is the largest web font size which we'll try to transcode. |
| // TODO(bashi): 30MB seems low. Update the limit if necessary. |
| static const size_t kMaxWebFontSize = 30 * 1024 * 1024; // 30 MB |
| if (buffer->size() > kMaxWebFontSize) { |
| SetErrorString("Web font size more than 30MB"); |
| return nullptr; |
| } |
| |
| // Most web fonts are compressed, so the result can be much larger than |
| // the original. |
| ots::ExpandingMemoryStream output(buffer->size(), kMaxWebFontSize); |
| double start = CurrentTime(); |
| BlinkOTSContext ots_context; |
| const char* data = buffer->Data(); |
| |
| TRACE_EVENT_BEGIN0("blink", "DecodeFont"); |
| bool ok = ots_context.Process(&output, reinterpret_cast<const uint8_t*>(data), |
| buffer->size()); |
| TRACE_EVENT_END0("blink", "DecodeFont"); |
| |
| if (!ok) { |
| SetErrorString(ots_context.GetErrorString()); |
| return nullptr; |
| } |
| |
| const size_t decoded_length = output.Tell(); |
| RecordDecodeSpeedHistogram(data, buffer->size(), CurrentTime() - start, |
| decoded_length); |
| |
| sk_sp<SkData> sk_data = SkData::MakeWithCopy(output.get(), decoded_length); |
| SkMemoryStream* stream = new SkMemoryStream(sk_data); |
| #if OS(WIN) |
| sk_sp<SkTypeface> typeface( |
| FontCache::GetFontCache()->FontManager()->createFromStream(stream)); |
| #else |
| sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(stream); |
| #endif |
| if (!typeface) { |
| SetErrorString("Not a valid font data"); |
| return nullptr; |
| } |
| |
| decoded_size_ = decoded_length; |
| return typeface; |
| } |
| |
| } // namespace blink |