1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkFontMgr.h"
13 #include "include/core/SkFontStyle.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPixmap.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkStream.h"
18 #include "include/core/SkSurface.h"
19 #include "include/core/SkTypeface.h"
20 #include "include/core/SkTypes.h"
21 #include "include/encode/SkJpegEncoder.h"
22 #include "include/ports/SkFontMgr_empty.h"
23 #include "modules/skparagraph/include/DartTypes.h"
24 #include "modules/skparagraph/include/FontCollection.h"
25 #include "modules/skparagraph/include/Paragraph.h"
26 #include "modules/skparagraph/include/ParagraphBuilder.h"
27 #include "modules/skparagraph/include/ParagraphStyle.h"
28 #include "modules/skunicode/include/SkUnicode_icu.h"
29
30 #include <cstdio>
31 #include <cstdlib>
32 #include <memory>
33
34 // https://www.gutenberg.org/ebooks/72339
35 constexpr const char* story =
36 "The landing port at Titan had not changed much in five years.\n"
37 "The ship settled down on the scarred blast shield, beside the same trio "
38 "of squat square buildings, and quickly disgorged its scanty quota of "
39 "cargo and a lone passenger into the flexible tube that linked the loading "
40 "hatch with the main building.\n"
41 "As soon as the tube was disconnected, the ship screamed off through the "
42 "murky atmosphere, seemingly glad to get away from Titan and head back to "
43 "the more comfortable and settled parts of the Solar System.";
44
45 class OneFontStyleSet : public SkFontStyleSet {
46 public:
OneFontStyleSet(sk_sp<SkTypeface> face)47 explicit OneFontStyleSet(sk_sp<SkTypeface> face) : face_(face) {}
48
49 protected:
count()50 int count() override { return 1; }
getStyle(int,SkFontStyle * out_style,SkString *)51 void getStyle(int, SkFontStyle* out_style, SkString*) override {
52 *out_style = SkFontStyle();
53 }
createTypeface(int index)54 sk_sp<SkTypeface> createTypeface(int index) override { return face_; }
matchStyle(const SkFontStyle &)55 sk_sp<SkTypeface> matchStyle(const SkFontStyle&) override { return face_; }
56
57 private:
58 sk_sp<SkTypeface> face_;
59 };
60
61 class OneFontMgr : public SkFontMgr {
62 public:
OneFontMgr(sk_sp<SkTypeface> face)63 explicit OneFontMgr(sk_sp<SkTypeface> face)
64 : face_(face), style_set_(sk_make_sp<OneFontStyleSet>(face)) {}
65
66 protected:
onCountFamilies() const67 int onCountFamilies() const override { return 1; }
onGetFamilyName(int index,SkString * familyName) const68 void onGetFamilyName(int index, SkString* familyName) const override {
69 *familyName = SkString("the-only-font-I-have");
70 }
onCreateStyleSet(int index) const71 sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override {
72 return style_set_;
73 }
onMatchFamily(const char[]) const74 sk_sp<SkFontStyleSet> onMatchFamily(const char[]) const override {
75 return style_set_;
76 }
77
onMatchFamilyStyle(const char[],const SkFontStyle &) const78 sk_sp<SkTypeface> onMatchFamilyStyle(const char[],
79 const SkFontStyle&) const override {
80 return face_;
81 }
onMatchFamilyStyleCharacter(const char familyName[],const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character) const82 sk_sp<SkTypeface> onMatchFamilyStyleCharacter(
83 const char familyName[], const SkFontStyle& style, const char* bcp47[],
84 int bcp47Count, SkUnichar character) const override {
85 return face_;
86 }
onLegacyMakeTypeface(const char[],SkFontStyle) const87 sk_sp<SkTypeface> onLegacyMakeTypeface(const char[],
88 SkFontStyle) const override {
89 return face_;
90 }
91
onMakeFromData(sk_sp<SkData>,int) const92 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int) const override {
93 std::abort();
94 return nullptr;
95 }
onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,int) const96 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
97 int) const override {
98 std::abort();
99 return nullptr;
100 }
onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,const SkFontArguments &) const101 sk_sp<SkTypeface> onMakeFromStreamArgs(
102 std::unique_ptr<SkStreamAsset>, const SkFontArguments&) const override {
103 std::abort();
104 return nullptr;
105 }
onMakeFromFile(const char[],int) const106 sk_sp<SkTypeface> onMakeFromFile(const char[], int) const override {
107 std::abort();
108 return nullptr;
109 }
110
111 private:
112 sk_sp<SkTypeface> face_;
113 sk_sp<SkFontStyleSet> style_set_;
114 };
115
main(int argc,char ** argv)116 int main(int argc, char** argv) {
117 if (argc != 3) {
118 printf("Usage: %s <font.ttf> <name.jpg>", argv[0]);
119 return 1;
120 }
121
122 SkFILEStream input(argv[1]);
123 if (!input.isValid()) {
124 printf("Cannot open input file %s\n", argv[1]);
125 return 1;
126 }
127 sk_sp<SkData> font_data = SkData::MakeFromStream(&input, input.getLength());
128 sk_sp<SkFontMgr> mgr = SkFontMgr_New_Custom_Empty();
129 sk_sp<SkTypeface> face = mgr->makeFromData(font_data);
130 if (!face) {
131 printf("input font %s was not parsable by Freetype\n", argv[1]);
132 return 1;
133 }
134
135 SkFILEWStream output(argv[2]);
136 if (!output.isValid()) {
137 printf("Cannot open output file %s\n", argv[2]);
138 return 1;
139 }
140
141 auto fontCollection = sk_make_sp<skia::textlayout::FontCollection>();
142 sk_sp<SkFontMgr> one_mgr = sk_make_sp<OneFontMgr>(face);
143 fontCollection->setDefaultFontManager(one_mgr);
144
145 constexpr int width = 200;
146 sk_sp<SkSurface> surface =
147 SkSurfaces::Raster(SkImageInfo::MakeN32(width, 200, kOpaque_SkAlphaType));
148 SkCanvas* canvas = surface->getCanvas();
149 canvas->clear(SK_ColorWHITE);
150
151 SkPaint paint;
152 paint.setAntiAlias(true);
153 paint.setColor(SK_ColorBLACK);
154
155 skia::textlayout::TextStyle style;
156 style.setForegroundColor(paint);
157 style.setFontFamilies({SkString("sans-serif")});
158 style.setFontSize(10.5);
159 skia::textlayout::ParagraphStyle paraStyle;
160 paraStyle.setTextStyle(style);
161 paraStyle.setTextAlign(skia::textlayout::TextAlign::kRight);
162
163 sk_sp<SkUnicode> unicode = SkUnicodes::ICU::Make();
164 if (!unicode) {
165 printf("Could not load unicode data\n");
166 return 1;
167 }
168 using skia::textlayout::ParagraphBuilder;
169 std::unique_ptr<ParagraphBuilder> builder =
170 ParagraphBuilder::make(paraStyle, fontCollection, unicode);
171 builder->addText(story);
172
173 std::unique_ptr<skia::textlayout::Paragraph> paragraph = builder->Build();
174 paragraph->layout(width - 20);
175 paragraph->paint(canvas, 10, 10);
176
177 SkPixmap pixmap;
178 if (surface->peekPixels(&pixmap)) {
179 if (!SkJpegEncoder::Encode(&output, pixmap, {})) {
180 printf("Cannot write output\n");
181 return 1;
182 }
183 } else {
184 printf("Cannot readback on surface\n");
185 return 1;
186 }
187
188 return 0;
189 }
190