Skip to content
This repository has been archived by the owner on Oct 2, 2020. It is now read-only.

Commit

Permalink
Gradients support (#41)
Browse files Browse the repository at this point in the history
* fix: gradients may start from incorrect place

* Initial stable release
  • Loading branch information
L2jLiga authored Nov 13, 2018
1 parent db6ca79 commit d22f43c
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 44 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.0.0]
### Fix
- gradients may start from incorrect place

## [1.0.0-4]
### Changed:
- Multiple output format as default
- Remove empty style rules while converting
Expand Down Expand Up @@ -306,8 +311,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add Artboard parser

[Unreleased]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-3...HEAD
[1.0.0-3]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-1...v1.0.0-3
[Unreleased]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-4...v1.0.0
[1.0.0-4]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-3...v1.0.0-4
[1.0.0-3]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-2...v1.0.0-3
[1.0.0-2]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-1...v1.0.0-2
[1.0.0-1]: https://github.com/L2jLiga/xd2svg/compare/v1.0.0-0...v1.0.0-1
[1.0.0-0]: https://github.com/L2jLiga/xd2svg/compare/v0.8.1...v1.0.0-0
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xd2svg",
"version": "1.0.0-4",
"version": "1.0.0",
"description": "Utility for converting Adobe XD files (*.xd) to SVG",
"keywords": [
"svg",
Expand Down
2 changes: 1 addition & 1 deletion src/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { manifestParser } from './manifest-parser
import { Artboard, ArtboardInfo, Dictionary, Directory } from './models';
import { resourcesParser } from './resources-parser';
import { svgo } from './svgo';
import { defs } from './utils/defs-list';
import { defs } from './utils';

interface InjectableSvgData {
defs: string;
Expand Down
13 changes: 6 additions & 7 deletions src/core/resources-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
*/

import { readFileSync } from 'fs';
import * as builder from 'xmlbuilder';
import { createElem } from './artboard-converter';
import { ArtboardInfo, Dictionary, Directory } from './models';
import { Color } from './styles/models';
import { colorTransformer } from './utils/color-transformer';
import { defs } from './utils/defs-list';
import { colorTransformer, defs, gradients } from './utils';

export function resourcesParser(directory: Directory): Dictionary<ArtboardInfo> {
const json = readFileSync(`${directory.name}/resources/graphics/graphicContent.agc`, 'utf-8');
Expand Down Expand Up @@ -41,14 +41,13 @@ function buildArtboardsInfo(artboards: { [id: string]: any }): Dictionary<Artboa
return artboardsInfoList;
}

function buildGradients(gradients): void {
Object
.keys(gradients)
.forEach((gradientId: string) => buildElement(gradients[gradientId], gradientId));
function buildGradients(list: Dictionary<{ type, stops }>): void {
Object.entries(list).forEach(([gradientId, gradient]) => buildElement(gradient, gradientId));
}

function buildElement({type, stops}, gradientId: string): void {
const gradient = defs.element(type === 'linear' ? 'linearGradient' : 'radialGradient', {id: gradientId});
const gradient = builder.begin().element(type === 'linear' ? 'linearGradient' : 'radialGradient', {id: gradientId});
gradients[gradientId] = gradient;

stops.forEach((stop: { offset: string, color: Color }) => {
gradient.element('stop', {
Expand Down
38 changes: 33 additions & 5 deletions src/core/styles/fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@
* found in the LICENSE file at https://github.com/L2jLiga/xd2svg/LICENSE
*/

import { XMLElementOrXMLNode } from 'xmlbuilder';
import { manifestInfo } from '../manifest-parser';
import { colorTransformer } from '../utils/color-transformer';
import { Fill, Parser, Pattern } from './models';
import { XMLElementOrXMLNode } from 'xmlbuilder';
import { manifestInfo } from '../manifest-parser';
import { colorTransformer, gradients } from '../utils';
import { Fill, Parser, Pattern } from './models';

export const fill: Parser = {
name: 'fill',
parse: fillParser,
};

export function fillParser(src: Fill, defs: XMLElementOrXMLNode): string {

switch (src.type) {
case 'color':
return colorTransformer(src.fill.color);
case 'gradient':
return `url(#${src.gradient.ref})`;
const gradientId: string = makeGradient(src.gradient, defs);

return `url(#${gradientId})`;
case 'pattern':
createPattern(src.pattern, defs);

Expand All @@ -33,6 +36,31 @@ export function fillParser(src: Fill, defs: XMLElementOrXMLNode): string {
}
}

function makeGradient(gradientInfo: Fill['gradient'], defs: XMLElementOrXMLNode) {
const gradient: XMLElementOrXMLNode = gradients[gradientInfo.ref].clone();
const gradientId = 'gradient-' + Object.values(gradientInfo).join('-');

gradient.attribute('id', gradientId);
applyIfPossible(gradient, 'gradientUnits', gradientInfo.units);
applyIfPossible(gradient, 'x1', gradientInfo.x1);
applyIfPossible(gradient, 'x2', gradientInfo.x2);
applyIfPossible(gradient, 'y1', gradientInfo.y1);
applyIfPossible(gradient, 'y2', gradientInfo.y2);
applyIfPossible(gradient, 'cx', gradientInfo.cx);
applyIfPossible(gradient, 'cy', gradientInfo.cy);
applyIfPossible(gradient, 'r', gradientInfo.r);

defs.importDocument(gradient);

return gradientId;
}

function applyIfPossible(ele: XMLElementOrXMLNode, name: string, value: any): void {
if (value) {
ele.att(name, value);
}
}

function createPattern(patternObject: Pattern, defs: XMLElementOrXMLNode): void {
const resources = manifestInfo.resources;
const pattern = defs.element('pattern', {
Expand Down
6 changes: 3 additions & 3 deletions src/core/styles/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
*/

import { XMLElementOrXMLNode } from 'xmlbuilder';
import { camelToDash } from '../utils/camel-to-dash';
import { colorTransformer } from '../utils/color-transformer';
import { Parser } from './models';
import { camelToDash } from '../utils/camel-to-dash';
import { colorTransformer } from '../utils/color-transformer';
import { Parser } from './models';

export const filters: Parser = {
name: 'filter',
Expand Down
13 changes: 13 additions & 0 deletions src/core/styles/models/fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,20 @@ export interface Fill {
color: Color;
};
gradient?: {
// Common gradients
units: 'objectBoundingBox' | 'userSpaceOnUse';
ref: string;

// Linear gradients
x1?: number;
y1?: number;
x2?: number;
y2?: number;

// Radial gradients
cx?: number;
cy?: number;
r?: number;
};
pattern?: Pattern;
color?: Color;
Expand Down
4 changes: 4 additions & 0 deletions src/core/utils/gradients-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { XMLElementOrXMLNode } from 'xmlbuilder';
import { Dictionary } from '../models';

export const gradients: Dictionary<XMLElementOrXMLNode> = {};
4 changes: 4 additions & 0 deletions src/core/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { camelToDash } from './camel-to-dash';
export { colorTransformer } from './color-transformer';
export { defs } from './defs-list';
export { gradients } from './gradients-list';
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { error, bold, red, blue } from './logger';
11 changes: 1 addition & 10 deletions test/core/resources-parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,6 @@ describe('Core > Resources parser', () => {

resourcesParser({name: ''} as any);

assert.equal(
defs.end(),
'<defs>' +
'<clipPath id="clipPathId"/>' +
'<linearGradient id="gradient1">' +
'<stop offset="0" stop-color="#fff"/>' +
'</linearGradient>' +
'<radialGradient id="gradient2"/>' +
'</defs>',
);
assert.equal(defs.end(), '<defs><clipPath id="clipPathId"/></defs>');
});
});
49 changes: 34 additions & 15 deletions test/core/styles/fill-stroke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
* found in the LICENSE file at https://github.com/L2jLiga/xd2svg/LICENSE
*/

import * as assert from 'assert';
import * as builder from 'xmlbuilder';
import { fill } from '../../../src/core/styles/fill';
import { Color, Pattern } from '../../../src/core/styles/models';
import { stroke } from '../../../src/core/styles/stroke';
import * as assert from 'assert';
import * as builder from 'xmlbuilder';
import { fill } from '../../../src/core/styles/fill';
import { Color, Fill, Pattern } from '../../../src/core/styles/models';
import { stroke } from '../../../src/core/styles/stroke';
import { gradients } from '../../../src/core/utils';

describe('Core > Styles parsers', () => {
const pattern: Pattern = {
Expand Down Expand Up @@ -64,11 +65,20 @@ describe('Core > Styles parsers', () => {
});

it('should return ref to gradient when type is gradient', () => {
const gradientRef = 'test-ref';

const result = fill.parse({type: 'gradient', gradient: {ref: gradientRef}});

assert.equal(result, `url(#${gradientRef})`);
const defs = builder.begin();
const gradientInfo: Fill['gradient'] = {
ref: 'ref',
units: 'objectBoundingBox',
x1: 1,
x2: 3,
y1: 2,
y2: 4,
};
gradients[gradientInfo.ref] = builder.begin().element('gradient');

const result = fill.parse({type: 'gradient', gradient: gradientInfo}, defs);

assert.equal(result, `url(#gradient-${Object.values(gradientInfo).join('-')})`);
});

it('should append pattern element to defs and return ref to this pattern if type is pattern', () => {
Expand Down Expand Up @@ -143,11 +153,20 @@ describe('Core > Styles parsers', () => {
});

it('should return ref to gradient when type is gradient', () => {
const gradientRef = 'test-ref';

const result = stroke.parse({type: 'gradient', gradient: {ref: gradientRef}});

assert.equal(result, `url(#${gradientRef})`);
const defs = builder.begin();
const gradientInfo: Fill['gradient'] = {
ref: 'ref',
units: 'objectBoundingBox',
x1: 1,
x2: 3,
y1: 2,
y2: 4,
};
gradients[gradientInfo.ref] = builder.begin().element('gradient');

const result = stroke.parse({type: 'gradient', gradient: gradientInfo}, defs);

assert.equal(result, `url(#gradient-${Object.values(gradientInfo).join('-')})`);
});

it('should append pattern element to defs and return ref to this pattern if type is pattern', () => {
Expand Down

0 comments on commit d22f43c

Please sign in to comment.