aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--BUILD.gn9
-rw-r--r--third_party/icu/BUILD.gn2
-rw-r--r--tools/SkShaper_harfbuzz.cpp129
-rw-r--r--tools/shape/SkShaper.h (renamed from tools/SkShaper.h)1
-rw-r--r--tools/shape/SkShaper_harfbuzz.cpp190
-rw-r--r--tools/shape/SkShaper_primitive.cpp (renamed from tools/SkShaper_primitive.cpp)3
-rw-r--r--tools/shape/using_skia_and_harfbuzz.cpp (renamed from tools/using_skia_and_harfbuzz.cpp)4
7 files changed, 204 insertions, 134 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 98bfdc5ba3..0e4c0715f6 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1389,19 +1389,20 @@ if (skia_enable_tools) {
if (!is_ios && !is_win) {
test_app("sktexttopdf-hb") {
sources = [
- "tools/SkShaper_harfbuzz.cpp",
- "tools/using_skia_and_harfbuzz.cpp",
+ "tools/shape/SkShaper_harfbuzz.cpp",
+ "tools/shape/using_skia_and_harfbuzz.cpp",
]
deps = [
":skia",
"//third_party/harfbuzz",
+ "//third_party/icu",
]
}
}
test_app("sktexttopdf") {
sources = [
- "tools/SkShaper_primitive.cpp",
- "tools/using_skia_and_harfbuzz.cpp",
+ "tools/shape/SkShaper_primitive.cpp",
+ "tools/shape/using_skia_and_harfbuzz.cpp",
]
deps = [
":skia",
diff --git a/third_party/icu/BUILD.gn b/third_party/icu/BUILD.gn
index 1d54c46edd..0a1683e81f 100644
--- a/third_party/icu/BUILD.gn
+++ b/third_party/icu/BUILD.gn
@@ -12,10 +12,12 @@ import("../third_party.gni")
if (skia_use_system_icu) {
system("icu") {
libs = [ "icuuc" ]
+ public_defines = [ "U_USING_ICU_NAMESPACE=0" ]
}
} else {
third_party("icu") {
public_include_dirs = [ "../externals/icu/source/common" ]
+ public_defines = [ "U_USING_ICU_NAMESPACE=0" ]
configs -= [ "//gn:no_rtti" ]
if (!is_win) {
libs = [ "dl" ]
diff --git a/tools/SkShaper_harfbuzz.cpp b/tools/SkShaper_harfbuzz.cpp
deleted file mode 100644
index 34fc712829..0000000000
--- a/tools/SkShaper_harfbuzz.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <hb-ot.h>
-
-#include "SkShaper.h"
-#include "SkStream.h"
-#include "SkTextBlob.h"
-#include "SkTypeface.h"
-
-static const int FONT_SIZE_SCALE = 512;
-
-namespace {
-struct HBFBlobDel {
- void operator()(hb_blob_t* b) { hb_blob_destroy(b); }
-};
-
-std::unique_ptr<hb_blob_t, HBFBlobDel> stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
- size_t size = asset->getLength();
- std::unique_ptr<hb_blob_t, HBFBlobDel> blob;
- if (const void* base = asset->getMemoryBase()) {
- blob.reset(hb_blob_create((char*)base, SkToUInt(size),
- HB_MEMORY_MODE_READONLY, asset.release(),
- [](void* p) { delete (SkStreamAsset*)p; }));
- } else {
- // SkDebugf("Extra SkStreamAsset copy\n");
- void* ptr = size ? sk_malloc_throw(size) : nullptr;
- asset->read(ptr, size);
- blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
- HB_MEMORY_MODE_READONLY, ptr, sk_free));
- }
- SkASSERT(blob);
- hb_blob_make_immutable(blob.get());
- return blob;
-}
-} // namespace
-
-struct SkShaper::Impl {
- struct HBFontDel {
- void operator()(hb_font_t* f) { hb_font_destroy(f); }
- };
- std::unique_ptr<hb_font_t, HBFontDel> fHarfBuzzFont;
- struct HBBufDel {
- void operator()(hb_buffer_t* b) { hb_buffer_destroy(b); }
- };
- std::unique_ptr<hb_buffer_t, HBBufDel> fBuffer;
- sk_sp<SkTypeface> fTypeface;
-};
-
-SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) {
- fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault();
- int index;
- std::unique_ptr<hb_blob_t, HBFBlobDel> blob(
- stream_to_blob(std::unique_ptr<SkStreamAsset>(
- fImpl->fTypeface->openStream(&index))));
- struct HBFaceDel {
- void operator()(hb_face_t* f) { hb_face_destroy(f); }
- };
- std::unique_ptr<hb_face_t, HBFaceDel> face(
- hb_face_create(blob.get(), (unsigned)index));
- SkASSERT(face);
- if (!face) {
- return;
- }
- hb_face_set_index(face.get(), (unsigned)index);
- hb_face_set_upem(face.get(), fImpl->fTypeface->getUnitsPerEm());
-
- fImpl->fHarfBuzzFont.reset(hb_font_create(face.get()));
- SkASSERT(fImpl->fHarfBuzzFont);
- hb_font_set_scale(fImpl->fHarfBuzzFont.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE);
- hb_ot_font_set_funcs(fImpl->fHarfBuzzFont.get());
-
- fImpl->fBuffer.reset(hb_buffer_create());
-}
-
-SkShaper::~SkShaper() {}
-
-bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; }
-
-SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
- const SkPaint& srcPaint,
- const char* utf8text,
- size_t textBytes,
- SkPoint point) const {
- SkPaint paint(srcPaint);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- paint.setTypeface(fImpl->fTypeface);
-
- SkASSERT(builder);
- hb_buffer_t* buffer = fImpl->fBuffer.get();
- hb_buffer_add_utf8(buffer, utf8text, -1, 0, -1);
- hb_buffer_guess_segment_properties(buffer);
- hb_shape(fImpl->fHarfBuzzFont.get(), buffer, nullptr, 0);
- unsigned len = hb_buffer_get_length(buffer);
- if (len == 0) {
- hb_buffer_clear_contents(buffer);
- return 0;
- }
-
- hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr);
- hb_glyph_position_t* pos =
- hb_buffer_get_glyph_positions(buffer, nullptr);
- auto runBuffer = builder->allocRunTextPos(
- paint, SkToInt(len), SkToInt(textBytes), SkString());
- memcpy(runBuffer.utf8text, utf8text, textBytes);
-
- double x = point.x();
- double y = point.y();
-
- double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE;
- double textSizeX = textSizeY * paint.getTextScaleX();
-
- for (unsigned i = 0; i < len; i++) {
- runBuffer.glyphs[i] = info[i].codepoint;
- runBuffer.clusters[i] = info[i].cluster;
- reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
- SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX),
- SkDoubleToScalar(y - pos[i].y_offset * textSizeY));
- x += pos[i].x_advance * textSizeX;
- y += pos[i].y_advance * textSizeY;
- }
-
- hb_buffer_clear_contents(buffer);
- return (SkScalar)x;
-}
diff --git a/tools/SkShaper.h b/tools/shape/SkShaper.h
index bc78be7d54..a2a301e792 100644
--- a/tools/SkShaper.h
+++ b/tools/shape/SkShaper.h
@@ -32,6 +32,7 @@ public:
const SkPaint& srcPaint,
const char* utf8text,
size_t textBytes,
+ bool leftToRight,
SkPoint point) const;
private:
diff --git a/tools/shape/SkShaper_harfbuzz.cpp b/tools/shape/SkShaper_harfbuzz.cpp
new file mode 100644
index 0000000000..3947406b85
--- /dev/null
+++ b/tools/shape/SkShaper_harfbuzz.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <hb-ot.h>
+#include <unicode/stringpiece.h>
+#include <unicode/ubidi.h>
+#include <unicode/unistr.h>
+
+#include "SkShaper.h"
+#include "SkStream.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+
+static const int FONT_SIZE_SCALE = 512;
+
+namespace {
+template <class T, void(*P)(T*)> using resource = std::unique_ptr<T, SkFunctionWrapper<void, T, P>>;
+using HBBlob = resource<hb_blob_t , hb_blob_destroy >;
+using HBFace = resource<hb_face_t , hb_face_destroy >;
+using HBFont = resource<hb_font_t , hb_font_destroy >;
+using HBBuffer = resource<hb_buffer_t, hb_buffer_destroy>;
+using ICUBiDi = resource<UBiDi , ubidi_close >;
+
+HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
+ size_t size = asset->getLength();
+ HBBlob blob;
+ if (const void* base = asset->getMemoryBase()) {
+ blob.reset(hb_blob_create((char*)base, SkToUInt(size),
+ HB_MEMORY_MODE_READONLY, asset.release(),
+ [](void* p) { delete (SkStreamAsset*)p; }));
+ } else {
+ // SkDebugf("Extra SkStreamAsset copy\n");
+ void* ptr = size ? sk_malloc_throw(size) : nullptr;
+ asset->read(ptr, size);
+ blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
+ HB_MEMORY_MODE_READONLY, ptr, sk_free));
+ }
+ SkASSERT(blob);
+ hb_blob_make_immutable(blob.get());
+ return blob;
+}
+} // namespace
+
+struct SkShaper::Impl {
+ HBFont fHarfBuzzFont;
+ HBBuffer fBuffer;
+ sk_sp<SkTypeface> fTypeface;
+};
+
+SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) {
+ fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault();
+ int index;
+ HBBlob blob(stream_to_blob(std::unique_ptr<SkStreamAsset>(
+ fImpl->fTypeface->openStream(&index))));
+ HBFace face(hb_face_create(blob.get(), (unsigned)index));
+ SkASSERT(face);
+ if (!face) {
+ return;
+ }
+ hb_face_set_index(face.get(), (unsigned)index);
+ hb_face_set_upem(face.get(), fImpl->fTypeface->getUnitsPerEm());
+
+ fImpl->fHarfBuzzFont.reset(hb_font_create(face.get()));
+ SkASSERT(fImpl->fHarfBuzzFont);
+ hb_font_set_scale(fImpl->fHarfBuzzFont.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE);
+ hb_ot_font_set_funcs(fImpl->fHarfBuzzFont.get());
+
+ fImpl->fBuffer.reset(hb_buffer_create());
+}
+
+SkShaper::~SkShaper() {}
+
+bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; }
+
+SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
+ const SkPaint& srcPaint,
+ const char* utf8text,
+ size_t textBytes,
+ bool leftToRight,
+ SkPoint point) const {
+ SkASSERT(builder);
+ UBiDiLevel bidiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
+ //hb_script_t script = ...
+ UErrorCode status = U_ZERO_ERROR;
+ double x = point.x();
+ double y = point.y();
+
+ // This function only accepts utf8.
+ // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
+ // Internally, harfbuzz is all utf32, but always makes a copy.
+
+ if (!SkTFitsIn<int32_t>(textBytes)) {
+ SkDebugf("Bidi error: text too long");
+ return (SkScalar)x;
+ }
+ icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(icu::StringPiece(utf8text, textBytes));
+
+ ICUBiDi bidi(ubidi_openSized(utf16.length(), 0, &status));
+ if (U_FAILURE(status)) {
+ SkDebugf("Bidi error: %s", u_errorName(status));
+ return (SkScalar)x;
+ }
+ SkASSERT(bidi);
+
+ ubidi_setPara(bidi.get(), utf16.getBuffer(), utf16.length(), bidiLevel, nullptr, &status);
+ if (U_FAILURE(status)) {
+ SkDebugf("Bidi error: %s", u_errorName(status));
+ return (SkScalar)x;
+ }
+
+ int32_t runCount = ubidi_countRuns(bidi.get(), &status);
+ if (U_FAILURE(status)) {
+ SkDebugf("Bidi error: %s", u_errorName(status));
+ return (SkScalar)x;
+ }
+
+ for (int32_t i = 0; i < runCount; ++i) {
+ int32_t start;
+ int32_t length;
+ UBiDiDirection direction = ubidi_getVisualRun(bidi.get(), i, &start, &length);
+
+ SkPaint paint(srcPaint);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setTypeface(fImpl->fTypeface);
+
+ hb_buffer_t* buffer = fImpl->fBuffer.get();
+ SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer);
+
+ // The difficulty here is the cluster mapping.
+ // If the hb_buffer is created with utf16, clusters will be pointing to the utf16 indexes,
+ // but the SkTextBlob can only take utf8 and utf8 cluster indexes.
+ // Instead of updating each cluster index, create the hb_buffer from the utf8.
+ // TODO: this is horribly inefficient.
+ const char* utf8textStart = utf8text;
+ const UChar* utf16Start = utf16.getBuffer();
+ while (utf16Start < utf16.getBuffer() + start) {
+ SkUTF16_NextUnichar(&utf16Start);
+ SkUTF8_NextUnichar(&utf8textStart);
+ }
+ const char* utf8textEnd = utf8textStart;
+ const UChar* utf16End = utf16Start;
+ while (utf16End < utf16.getBuffer() + start + length) {
+ SkUTF16_NextUnichar(&utf16End);
+ SkUTF8_NextUnichar(&utf8textEnd);
+ }
+ size_t utf8runLength = utf8textEnd - utf8textStart;
+ if (!SkTFitsIn<int>(utf8runLength)) {
+ SkDebugf("Shaping error: utf8 too long");
+ return (SkScalar)x;
+ }
+ hb_buffer_add_utf8(buffer, utf8textStart, utf8runLength, 0, -1);
+ hb_buffer_guess_segment_properties(buffer);
+ //hb_buffer_set_script(buffer, script);
+ hb_buffer_set_direction(buffer, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
+ hb_shape(fImpl->fHarfBuzzFont.get(), buffer, nullptr, 0);
+ unsigned len = hb_buffer_get_length(buffer);
+ if (len == 0) {
+ continue;
+ }
+
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr);
+ hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr);
+
+ if (!SkTFitsIn<int>(len)) {
+ SkDebugf("Shaping error: too many glyphs");
+ return (SkScalar)x;
+ }
+ auto runBuffer = builder->allocRunTextPos(paint, len, utf8runLength, SkString());
+ memcpy(runBuffer.utf8text, utf8textStart, utf8runLength);
+
+ double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE;
+ double textSizeX = textSizeY * paint.getTextScaleX();
+
+ for (unsigned i = 0; i < len; i++) {
+ runBuffer.glyphs[i] = info[i].codepoint;
+ runBuffer.clusters[i] = info[i].cluster;
+ reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
+ SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX),
+ SkDoubleToScalar(y - pos[i].y_offset * textSizeY));
+ x += pos[i].x_advance * textSizeX;
+ y += pos[i].y_advance * textSizeY;
+ }
+ }
+ return (SkScalar)x;
+}
diff --git a/tools/SkShaper_primitive.cpp b/tools/shape/SkShaper_primitive.cpp
index b165285287..750c51621e 100644
--- a/tools/SkShaper_primitive.cpp
+++ b/tools/shape/SkShaper_primitive.cpp
@@ -33,7 +33,10 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
const SkPaint& srcPaint,
const char* utf8text,
size_t textBytes,
+ bool leftToRight,
SkPoint point) const {
+ sk_ignore_unused_variable(leftToRight);
+
SkPaint paint(srcPaint);
paint.setTypeface(fImpl->fTypeface);
paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
diff --git a/tools/using_skia_and_harfbuzz.cpp b/tools/shape/using_skia_and_harfbuzz.cpp
index d405c71313..3f1515fb1d 100644
--- a/tools/using_skia_and_harfbuzz.cpp
+++ b/tools/shape/using_skia_and_harfbuzz.cpp
@@ -150,7 +150,7 @@ public:
current_y = config->line_spacing_ratio.value * config->font_size.value;
}
SkTextBlobBuilder textBlobBuilder;
- shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, SkPoint{0, 0});
+ shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, true, SkPoint{0, 0});
sk_sp<const SkTextBlob> blob = textBlobBuilder.make();
pageCanvas->drawTextBlob(
blob.get(), SkDoubleToScalar(current_x),
@@ -207,10 +207,12 @@ int main(int argc, char **argv) {
}
SkShaper shaper(typeface);
assert(shaper.good());
+ //SkString line("This is هذا هو الخط a line.");
for (std::string line; std::getline(std::cin, line);) {
placement.WriteLine(shaper, line.c_str(), line.size());
}
doc->close();
+ wStream.flush();
return 0;
}