| /* | |
| * Copyright 2023 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkAlphaType.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkFontMgr.h" | |
| #include "include/core/SkFontStyle.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkPixmap.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkSurface.h" | |
| #include "include/core/SkTypeface.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/encode/SkJpegEncoder.h" | |
| #include "include/ports/SkFontMgr_empty.h" | |
| #include "modules/skparagraph/include/DartTypes.h" | |
| #include "modules/skparagraph/include/FontCollection.h" | |
| #include "modules/skparagraph/include/Paragraph.h" | |
| #include "modules/skparagraph/include/ParagraphBuilder.h" | |
| #include "modules/skparagraph/include/ParagraphStyle.h" | |
| #include "modules/skunicode/include/SkUnicode_icu.h" | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <memory> | |
| // https://www.gutenberg.org/ebooks/72339 | |
| constexpr const char* story = | |
| "The landing port at Titan had not changed much in five years.\n" | |
| "The ship settled down on the scarred blast shield, beside the same trio " | |
| "of squat square buildings, and quickly disgorged its scanty quota of " | |
| "cargo and a lone passenger into the flexible tube that linked the loading " | |
| "hatch with the main building.\n" | |
| "As soon as the tube was disconnected, the ship screamed off through the " | |
| "murky atmosphere, seemingly glad to get away from Titan and head back to " | |
| "the more comfortable and settled parts of the Solar System."; | |
| class OneFontStyleSet : public SkFontStyleSet { | |
| public: | |
| explicit OneFontStyleSet(sk_sp<SkTypeface> face) : face_(face) {} | |
| protected: | |
| int count() override { return 1; } | |
| void getStyle(int, SkFontStyle* out_style, SkString*) override { | |
| *out_style = SkFontStyle(); | |
| } | |
| sk_sp<SkTypeface> createTypeface(int index) override { return face_; } | |
| sk_sp<SkTypeface> matchStyle(const SkFontStyle&) override { return face_; } | |
| private: | |
| sk_sp<SkTypeface> face_; | |
| }; | |
| class OneFontMgr : public SkFontMgr { | |
| public: | |
| explicit OneFontMgr(sk_sp<SkTypeface> face) | |
| : face_(face), style_set_(sk_make_sp<OneFontStyleSet>(face)) {} | |
| protected: | |
| int onCountFamilies() const override { return 1; } | |
| void onGetFamilyName(int index, SkString* familyName) const override { | |
| *familyName = SkString("the-only-font-I-have"); | |
| } | |
| sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override { | |
| return style_set_; | |
| } | |
| sk_sp<SkFontStyleSet> onMatchFamily(const char[]) const override { | |
| return style_set_; | |
| } | |
| sk_sp<SkTypeface> onMatchFamilyStyle(const char[], | |
| const SkFontStyle&) const override { | |
| return face_; | |
| } | |
| sk_sp<SkTypeface> onMatchFamilyStyleCharacter( | |
| const char familyName[], const SkFontStyle& style, const char* bcp47[], | |
| int bcp47Count, SkUnichar character) const override { | |
| return face_; | |
| } | |
| sk_sp<SkTypeface> onLegacyMakeTypeface(const char[], | |
| SkFontStyle) const override { | |
| return face_; | |
| } | |
| sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int) const override { | |
| std::abort(); | |
| return nullptr; | |
| } | |
| sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, | |
| int) const override { | |
| std::abort(); | |
| return nullptr; | |
| } | |
| sk_sp<SkTypeface> onMakeFromStreamArgs( | |
| std::unique_ptr<SkStreamAsset>, const SkFontArguments&) const override { | |
| std::abort(); | |
| return nullptr; | |
| } | |
| sk_sp<SkTypeface> onMakeFromFile(const char[], int) const override { | |
| std::abort(); | |
| return nullptr; | |
| } | |
| private: | |
| sk_sp<SkTypeface> face_; | |
| sk_sp<SkFontStyleSet> style_set_; | |
| }; | |
| int main(int argc, char** argv) { | |
| if (argc != 3) { | |
| printf("Usage: %s <font.ttf> <name.jpg>", argv[0]); | |
| return 1; | |
| } | |
| SkFILEStream input(argv[1]); | |
| if (!input.isValid()) { | |
| printf("Cannot open input file %s\n", argv[1]); | |
| return 1; | |
| } | |
| sk_sp<SkData> font_data = SkData::MakeFromStream(&input, input.getLength()); | |
| sk_sp<SkFontMgr> mgr = SkFontMgr_New_Custom_Empty(); | |
| sk_sp<SkTypeface> face = mgr->makeFromData(font_data); | |
| if (!face) { | |
| printf("input font %s was not parsable by Freetype\n", argv[1]); | |
| return 1; | |
| } | |
| SkFILEWStream output(argv[2]); | |
| if (!output.isValid()) { | |
| printf("Cannot open output file %s\n", argv[2]); | |
| return 1; | |
| } | |
| auto fontCollection = sk_make_sp<skia::textlayout::FontCollection>(); | |
| sk_sp<SkFontMgr> one_mgr = sk_make_sp<OneFontMgr>(face); | |
| fontCollection->setDefaultFontManager(one_mgr); | |
| constexpr int width = 200; | |
| sk_sp<SkSurface> surface = | |
| SkSurfaces::Raster(SkImageInfo::MakeN32(width, 200, kOpaque_SkAlphaType)); | |
| SkCanvas* canvas = surface->getCanvas(); | |
| canvas->clear(SK_ColorWHITE); | |
| SkPaint paint; | |
| paint.setAntiAlias(true); | |
| paint.setColor(SK_ColorBLACK); | |
| skia::textlayout::TextStyle style; | |
| style.setForegroundColor(paint); | |
| style.setFontFamilies({SkString("sans-serif")}); | |
| style.setFontSize(10.5); | |
| skia::textlayout::ParagraphStyle paraStyle; | |
| paraStyle.setTextStyle(style); | |
| paraStyle.setTextAlign(skia::textlayout::TextAlign::kRight); | |
| sk_sp<SkUnicode> unicode = SkUnicodes::ICU::Make(); | |
| if (!unicode) { | |
| printf("Could not load unicode data\n"); | |
| return 1; | |
| } | |
| using skia::textlayout::ParagraphBuilder; | |
| std::unique_ptr<ParagraphBuilder> builder = | |
| ParagraphBuilder::make(paraStyle, fontCollection, unicode); | |
| builder->addText(story); | |
| std::unique_ptr<skia::textlayout::Paragraph> paragraph = builder->Build(); | |
| paragraph->layout(width - 20); | |
| paragraph->paint(canvas, 10, 10); | |
| SkPixmap pixmap; | |
| if (surface->peekPixels(&pixmap)) { | |
| if (!SkJpegEncoder::Encode(&output, pixmap, {})) { | |
| printf("Cannot write output\n"); | |
| return 1; | |
| } | |
| } else { | |
| printf("Cannot readback on surface\n"); | |
| return 1; | |
| } | |
| return 0; | |
| } |