Skip to content

Commit

Permalink
Merge pull request #334 from Brooooooklyn/svg-text-to-path
Browse files Browse the repository at this point in the history
feat: implement convertSVGTextToPath function
  • Loading branch information
Brooooooklyn authored Aug 5, 2021
2 parents 785a344 + 9c7ca98 commit f4f2499
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 25 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target
depot_tools
skia
skia-c
__test__/text-to-path.svg
22 changes: 22 additions & 0 deletions __test__/svg.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { join } from 'path'
import { readFileSync, promises as fs } from 'fs'
import { platform } from 'os'

import test from 'ava'

import { convertSVGTextToPath, GlobalFonts } from '../index'

GlobalFonts.registerFromPath(join(__dirname, 'fonts', 'iosevka-slab-regular.ttf'))

const FIXTURE = readFileSync(join(__dirname, 'text.svg'), 'utf8')

test('convertSVGTextToPath should work', async (t) => {
const result = convertSVGTextToPath(FIXTURE)
const outputPath = join(__dirname, 'text-to-path.svg')
const output = await fs.readFile(outputPath, 'utf8')
if (platform() === 'win32') {
t.true(result.toString('utf8').length > 13000)
} else {
t.deepEqual(result.toString('utf8'), output)
}
})
12 changes: 12 additions & 0 deletions __test__/text-to-path.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions __test__/text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified example/draw-emoji.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,5 @@ export const enum SvgExportFlag {
NoPrettyXML = 0x02,
RelativePathEncoding = 0x04,
}

export function convertSVGTextToPath(svg: Buffer | string): Buffer
18 changes: 16 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ const { loadBinding } = require('@node-rs/helper')
* loadBinding helper will load `skia.[PLATFORM].node` from `__dirname` first
* If failed to load addon, it will fallback to load from `@napi-rs/skia-[PLATFORM]`
*/
const { CanvasRenderingContext2D, CanvasElement, SVGCanvas, Path2D, ImageData, Image, CanvasPattern, GlobalFonts } =
loadBinding(__dirname, 'skia', '@napi-rs/canvas')
const {
CanvasRenderingContext2D,
CanvasElement,
SVGCanvas,
Path2D,
ImageData,
Image,
CanvasPattern,
GlobalFonts,
convertSVGTextToPath: _convertSVGTextToPath,
} = loadBinding(__dirname, 'skia', '@napi-rs/canvas')

const Geometry = require('./geometry')

Expand Down Expand Up @@ -193,6 +202,10 @@ if (!process.env.DISABLE_SYSTEM_FONTS_LOAD) {
FamilyNamesSet = JSON.parse(GlobalFontsSingleton._families)
}

function convertSVGTextToPath(input) {
return _convertSVGTextToPath(Buffer.from(input), GlobalFontsSingleton)
}

module.exports = {
createCanvas,
Path2D,
Expand All @@ -205,4 +218,5 @@ module.exports = {
SvgExportFlag,
...Geometry,
GlobalFonts: GlobalFontsSingleton,
convertSVGTextToPath,
}
17 changes: 17 additions & 0 deletions skia-c/skia_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1268,4 +1268,21 @@ extern "C"
{
delete reinterpret_cast<SkDynamicMemoryWStream *>(c_w_memory_stream);
}

// SkSVG
void skiac_svg_text_to_path(const uint8_t *data, size_t length, skiac_font_collection *c_collection, skiac_sk_data *output_data)
{
auto svg_stream = new SkMemoryStream(data, length, false);
auto w_stream = new SkDynamicMemoryWStream();
auto svg_dom = SkSVGDOM::Builder().setFontManager(c_collection->assets).make(*svg_stream);
auto svg_root = svg_dom->getRoot();
auto svg_container_size = svg_root->intrinsicSize(SkSVGLengthContext(SkSize::Make(0, 0)));
auto canvas = SkSVGCanvas::Make(SkRect::MakeSize(svg_container_size), w_stream, SkSVGCanvas::kConvertTextToPaths_Flag);
svg_dom->render(canvas.get());
canvas.reset();
auto d = w_stream->detachAsData().release();
output_data->data = reinterpret_cast<skiac_data *>(d);
output_data->size = d->size();
output_data->ptr = d->bytes();
}
}
41 changes: 39 additions & 2 deletions skia-c/skia_c.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
#include <include/core/SkBitmap.h>
#include <include/core/SkCanvas.h>
#include <include/core/SkData.h>
#include <include/core/SkDrawable.h>
#include <include/core/SkGraphics.h>
#include <include/core/SkFontMgr.h>
#include <include/core/SkPaint.h>
#include <include/core/SkPathEffect.h>
#include <include/core/SkSurface.h>
#include <include/core/SkMaskFilter.h>
#include <include/core/SkStream.h>
#include <include/core/SkPictureRecorder.h>
#include <include/core/SkStrokeRec.h>
#include <include/effects/SkDashPathEffect.h>
#include <include/effects/SkTrimPathEffect.h>
Expand All @@ -30,9 +33,11 @@
#include <modules/skparagraph/include/TypefaceFontProvider.h>
#include <modules/svg/include/SkSVGDOM.h>
#include <modules/svg/include/SkSVGSVG.h>
#include <modules/svg/include/SkSVGNode.h>
#include <modules/svg/include/SkSVGRenderContext.h>
#include <src/ports/SkFontMgr_custom.h>
#include <src/core/SkFontDescriptor.h>
#include <src/xml/SkXMLWriter.h>

#include <stdint.h>

Expand Down Expand Up @@ -69,6 +74,34 @@ enum class CssBaseline
Bottom,
};

class TypefaceFontProviderCustom : public TypefaceFontProvider
{
public:
explicit TypefaceFontProviderCustom(sk_sp<SkFontMgr> mgr) : font_mgr(std::move(mgr))
{
}

~TypefaceFontProviderCustom(){};

sk_sp<SkTypeface> onLegacyMakeTypeface(const char family_name[], SkFontStyle style) const override
{
auto style_set = this->onMatchFamily(family_name);
if (!style_set)
{
return nullptr;
}
auto tf = style_set->matchStyle(style);
if (!tf)
{
return nullptr;
}
return sk_sp<SkTypeface>(const_cast<SkTypeface *>(tf));
}

private:
sk_sp<SkFontMgr> font_mgr;
};

struct skiac_svg_surface
{
skiac_w_memory_stream *stream;
Expand All @@ -80,12 +113,13 @@ struct skiac_font_collection
{
sk_sp<FontCollection> collection;
sk_sp<SkFontMgr> font_mgr;
sk_sp<TypefaceFontProvider> assets;
skiac_font_collection() : collection(sk_make_sp<FontCollection>()), font_mgr(SkFontMgr_New_Custom_Empty()), assets(sk_make_sp<TypefaceFontProvider>())
sk_sp<TypefaceFontProviderCustom> assets;
skiac_font_collection() : collection(sk_make_sp<FontCollection>()), font_mgr(SkFontMgr_New_Custom_Empty()), assets(sk_make_sp<TypefaceFontProviderCustom>(font_mgr))
{
collection->setDefaultFontManager(font_mgr);
collection->setAssetFontManager(assets);
collection->enableFontFallback();
assets->ref();
}
};

Expand Down Expand Up @@ -374,6 +408,9 @@ extern "C"
// SkDynamicMemoryWStream
void skiac_sk_w_stream_get(skiac_w_memory_stream *c_w_memory_stream, skiac_sk_data *sk_data, int width, int height);
void skiac_sk_w_stream_destroy(skiac_w_memory_stream *c_w_memory_stream);

// SkSVG
void skiac_svg_text_to_path(const uint8_t *data, size_t length, skiac_font_collection *c_collection, skiac_sk_data *output_data);
}

#endif // SKIA_CAPI_H
2 changes: 1 addition & 1 deletion src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2022,7 +2022,7 @@ unsafe impl Send for ContextData {}
unsafe impl Sync for ContextData {}

impl Task for ContextData {
type Output = SurfaceDataRef;
type Output = SkiaDataRef;
type JsValue = JsBuffer;

fn compute(&mut self) -> Result<Self::Output> {
Expand Down
Loading

0 comments on commit f4f2499

Please sign in to comment.