Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

gerber-to-svg

Package Overview
Dependencies
Maintainers
1
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gerber-to-svg - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

lib/clone.js

175

API.md

@@ -13,2 +13,6 @@ # gerber-to-svg API

- [input](#input)
- [options](#options)
- [id and attributes options](#id-and-attributes-options)
- [element options](#element-options)
- [parsing and plotting options](#parsing-and-plotting-options)
- [streaming API](#streaming-api)

@@ -21,8 +25,2 @@ - [callback API](#callback-api)

- [output](#output)
- [options](#options)
- [id option](#id-option)
- [class option](#class-option)
- [color option](#color-option)
- [pretty option](#pretty-option)
- [parsing and plotting options](#parsing-and-plotting-options)
- [public properties](#public-properties)

@@ -56,2 +54,75 @@ - [parser and plotter](#parser-and-plotter)

### options
The function must be passed an options object or string. If passed a string, it will be used as the `options.attribute.id` value. You may also use `options.id` in place of `options.attributes.id`. An `id` must be defined. The following options are available:
**svg options**
key | value | default
-----------------|----------|--------------------------------------------------
id | String | See below
attributes | Object | See below
createElement | Function | [`xmlElementString`](./lib/xml-element-string)
includeNamespace | Boolean | `true`
objectMode | Boolean | false
**parsing and plotting options**
key | value | default
--------------|---------------------|------------------
places | [int, int] | Parsed from file
zero | 'L' or 'T' | Parsed from file
filetype | 'gerber' or 'drill' | Parsed from file
units | `mm` or `in` | Parsed from file
backupUnits | `mm` or `in` | 'in'
nota | `A` or `I` | Parsed from file
backupNota | `A` or `I` | 'A'
optimizePaths | `true` or `false` | `false`
plotAsOutline | `true` or `false` | `false`
#### id and attributes options
The `id` and `attributes` options are used to set additional attributes of the top-level SVG node. Either the `id` option or `attributes.id` is required. `id` should be unique to avoid display problems with multiple SVGs on the same page.
Some good candidates for other attributes to specify are:
* `color` - Fills and strokes are set to `currentColor`, so setting color will change the color of the entire layer
* `width` and `height` - By default, the width and height will be the real world dimensions of the layer, but you may want to set them to `100%` for display purposes
* `class` or `className` (depending on your `createElement` function) - self explanatory
#### element options
`createElement`, `includeNamespace` and `objectMode` allow you to generate renders in a different format than the default XML string. `createElement` is a [hyperscript-style](https://github.com/dominictarr/hyperscript) function that takes a tagName, attributes map, and children array:
``` javascript
var defaultCreateElement = require('gerber-to-svg/lib/xml-elements-string')
var customCreateElement = function(tagName, attributes, children) {
// create an element somehow
return 🍩
}
```
The `includeNamespace` attribute is complementary to the `createElement` function, and determines whether the `xmlns: 'http://www.w3.org/2000/svg'` attribute will be included in or omitted from the `attributes` parameter of `createElement`. In certain virtual-dom implementations, you will need to set `includeNamespace` to `false` to avoid problems with `document.createElementNS`.
`objectMode` needs to be set according to whether `createElement` returns a string (`objectMode: false`) or anything else (`objectMode: true`). If `objectMode` is set to true, the converter stream will be in [object mode](https://nodejs.org/api/stream.html#stream_object_mode) and emit objects instead of buffers.
#### parsing and plotting options
These options are available in case you have an older or poorly formatted file that does not contain the necessary format information and does not conform to the assumptions this library makes in the absence of that information. Some knowledge of the Gerber and Excellon formats are helpful when trying to understand these options.
For more information, please reference the API documentation of [gerber-parser](https://github.com/mcous/gerber-parser/blob/master/API.md) and [gerber-plotter](https://github.com/mcous/gerber-plotter/blob/master/API.md), as these options are passed directly to these modules.
key | description
--------------|-----------------------------------------------------------------------------
places | The number of places before and after the decimal used in coordinates
zero | Leading or trailing zero suppression used in coordinates
filetype | Whether to parse the file as a Gerber file or as a NC drill (Excellon) file
units | Units of the file
backupUnits | Backup units only to be used if units cannot be parsed from the file
nota | Absolute or incremental coordinate notation
backupNota | Backup notation only to be used if the notation cannot be parsed
optimizePaths | Optimize trace paths by rearranging to occur in physical order
plotAsOutline | Optimize and correct paths to better render the layer as a board outline
## streaming API

@@ -107,4 +178,9 @@

var gerberToSvg = require('gerber-to-svg')
var cloneConverter = gerberToSvg.clone
// or, for smaller builds
var cloneConverter = require('gerber-to-svg/lib/clone')
var converter = gerberToSvg(input, options, function(error, result) {
var converterClone = gerberToSvg.clone(converter)
var converterClone = cloneConverter(converter)
storeSomehow(converterClone)

@@ -116,12 +192,27 @@ })

Returns the SVG string from a completed converter or a clone of a completed converter.
Returns the SVG from a completed converter or a clone of a completed `converter`.
`gerberToSvg.render(converter, [id], [className], [color])`
`gerberToSvg.render(converter, idOrAttributes, [createElement, includeNamespace])`
* `converter` is the original gerber-to-svg converter or a clone of it
* `idOrAttributes` is a string element id or an object of attributes
* If it is an object, an `id` field is required
* `createElement` is an optional function to use to create elements
* Default: [`xml-element-string`](./lib/xml-element-string.js)
* If used, must be the same `createElement` used in the original conversion
* The API of `createElement` is [hyperscript style](https://github.com/dominictarr/hyperscript): (tag, attributes, children) => element
* Can be used to create a VDOM element rather than an SVG string
* `includeNamespace` is an optional flag that determines whether the xmlns attribute is passed to `createElement` (defaults to `true`)
``` javascript
var gerberToSvg = require('gerber-to-svg')
var renderConverter = gerberToSvg.render
// or, for smaller builds
var renderConverter = require('gerber-to-svg/lib/render')
var converter = getConverterCloneSomehow()
var id = 'my-cool-id'
var svgString = gerberToSvg.render(converter, id)
var svgString = renderConverter(converter, id)
```

@@ -189,58 +280,2 @@

## options
The function must be passed an options object or string. If passed a string, it will be used as the `id` option. If passed an object, the `id` key is required. The following options are available:
**svg options**
key | value | default
-------|-----------|----------------
id | String | N/A (required)
class | String | undefined
color | CSS color | undefined
**parsing and plotting options**
key | value | default
--------------|---------------------|------------------
places | [int, int] | Parsed from file
zero | 'L' or 'T' | Parsed from file
filetype | 'gerber' or 'drill' | Parsed from file
units | `mm` or `in` | Parsed from file
backupUnits | `mm` or `in` | 'in'
nota | `A` or `I` | Parsed from file
backupNota | `A` or `I` | 'A'
optimizePaths | `true` or `false` | `false`
plotAsOutline | `true` or `false` | `false`
### id option
The `id` option is used as the id of the SVG node. It is also used to determine the prefix to the ids of internal nodes. If you are generating SVGs for multiple Gerber files for display on the same page, it is important that each conversion is passed a unique ID. If they are not unique, there may be id collisions between SVGs, and things could get weird. Any periods (`.`) in the id will be replaced with dashes (`-`).
### class option
The `class` option adds a class to the SVG node. Any periods (`.`) in the classname will be replaced with dashes (`-`).
### color option
The `color` option is a CSS color that will be used as the `fill` and `stroke` value of the image. The default value of 'currentColor' means the color will be defined by the CSS `color` property of the SVG.
### pretty option
If `pretty` is non-zero, the SVG string will be pretty-printed with tab-sizes equal to the number that was input.
### parsing and plotting options
These options are available in case you have an older or poorly formatted file that does not contain the necessary format information and does not conform to the assumptions this library makes in the absence of that information. Some knowledge of the Gerber and Excellon formats are helpful when trying to understand these options.
key | description
------------|-----------------------------------------------------------------------------
places | The number of places before and after the decimal used in coordinates
zero | Leading or trailing zero suppression used in coordinates
filetype | Whether to parse the file as a Gerber file or as a NC drill (Excellon) file
units | Units of the file
backupUnits | Backup units only to be used if units cannot be parsed from the file
nota | Absolute or incremental coordinate notation
backupNota | Backup notation only to be used if the notation cannot be parsed
## public properties

@@ -254,4 +289,4 @@

plotter | `gerber-plotter` plotter
defs | String
layer | String
defs | Array
layer | Array
viewBox | Array

@@ -268,7 +303,7 @@ width | String

The interior (innerText) of the `defs` node of the SVG. This is where pad shapes and clear layers will be defined.
An array of the interior elements of the `defs` node of the SVG. This is where pad shapes and clear layers will be defined.
### layer
The interior of the top-level `g` node of the SVG. This is where regions, strokes, and flashes of dark layers will be. If there are clear layers, there may be nested `g` nodes with `mask` attributes inside `layer`. If no image was produces, this property will be falsey.
An array of the interior elements of the top-level `g` node of the SVG. This is where regions, strokes, and flashes of dark layers will be. If there are clear layers, there may be nested `g` nodes with `mask` attributes inside `layer`. If no image was produces, this array will be empty.

@@ -275,0 +310,0 @@ ### viewBox

24

lib/_create-path.js
// create a path from a fill or stroke object
'use strict'
var reduce = require('lodash.reduce')
var util = require('./_util')
var shift = util.shift
var xmlNode = util.xmlNode

@@ -20,2 +17,3 @@ var pointsEqual = function(point, target) {

var cmd = (lastCmd === 'L' || lastCmd === 'M') ? '' : 'L '
return (cmd + shift(end[0]) + ' ' + shift(end[1]))

@@ -36,7 +34,9 @@ }

var arc2 = arc('A', radius, Math.PI, dir, end, center)
return arc1 + ' ' + arc2
}
var result = (lastCmd === 'A') ? '' : 'A '
radius = shift(radius)
var result = (lastCmd === 'A') ? '' : 'A '
result += radius + ' ' + radius + ' 0 '

@@ -82,10 +82,12 @@ result += ((sweep > Math.PI) ? '1 ' : '0 ')

var createPath = function(segments, width) {
var pathData = reduce(segments, reduceSegments, {last: [], data: ''}).data
var fill = (width != null) ? 'none' : null
width = (width != null) ? shift(width) : null
module.exports = function createPath(segments, width, element) {
var pathData = segments.reduce(reduceSegments, {last: [], data: ''}).data
var attr = {d: pathData}
return xmlNode('path', true, {d: pathData, fill: fill, 'stroke-width': width})
if (width != null) {
attr.fill = 'none'
attr['stroke-width'] = shift(width)
}
return element('path', attr)
}
module.exports = createPath

@@ -6,10 +6,7 @@ // creates the SVG for a pad flash

var shift = util.shift
var xmlNode = util.xmlNode
var flashPad = function(prefix, tool, x, y) {
module.exports = function flashPad(prefix, tool, x, y, element) {
var toolId = '#' + prefix + '_pad-' + tool
return xmlNode('use', true, {'xlink:href': toolId, x: shift(x), y: shift(y)})
return element('use', {'xlink:href': toolId, x: shift(x), y: shift(y)})
}
module.exports = flashPad
// reduce a shape array into a string to place is defs
'use strict'
var reduce = require('lodash.reduce')
var util = require('./_util')
var shift = util.shift
var xmlNode = util.xmlNode
var startMask = util.startMask
var createMask = util.createMask
var maskLayer = util.maskLayer
var circle = function(id, cx, cy, r, width) {
width = (width != null) ? shift(width) : null
var fill = (width != null) ? 'none' : null
var element = function(tag, attr, children) {
return {tag: tag, attr: attr, children: children || []}
}
return xmlNode('circle', true, {
id: id,
var circle = function(cx, cy, r, width) {
var attr = {
cx: shift(cx),
cy: shift(cy),
r: shift(r),
'stroke-width': width,
fill: fill
})
r: shift(r)
}
if (width != null) {
attr['stroke-width'] = shift(width)
attr.fill = 'none'
}
return element('circle', attr)
}
var rect = function(id, cx, cy, r, width, height) {
r = (r) ? shift(r) : null
return xmlNode('rect', true, {
id: id,
var rect = function(cx, cy, r, width, height) {
var attr = {
x: shift(cx - width / 2),
y: shift(cy - height / 2),
rx: r,
ry: r,
width: shift(width),
height: shift(height)
})
}
}
var polyPoints = function(result, point, i, points) {
var pointString = shift(point[0]) + ',' + shift(point[1])
return (result + pointString + ((i < (points.length - 1)) ? ' ' : ''))
if (r) {
attr.rx = shift(r)
attr.ry = shift(r)
}
return element('rect', attr)
}
var poly = function(id, points) {
return xmlNode('polygon', true, {id: id, points: reduce(points, polyPoints, '')})
var poly = function(points) {
var pointsAttr = points.map(function(point) {
return point.map(shift).join(',')
}).join(' ')
return element('polygon', {points: pointsAttr})
}
var clip = function(id, shapes, ring) {
var maskId = id + '_mask'
var clip = function(maskIdPrefix, index, shapes, ring, createElement) {
var maskId = maskIdPrefix + 'mask-' + index
var maskUrl = 'url(#' + maskId + ')'
var mask = xmlNode('mask', false, {id: maskId, stroke: '#fff'})
mask += circle(null, ring.cx, ring.cy, ring.r, ring.width) + '</mask>'
var group = reduce(shapes, function(result, shape) {
if (shape.type === 'rect') {
return (result + rect(null, shape.cx, shape.cy, shape.r, shape.width, shape.height))
}
var circleNode = circle(ring.cx, ring.cy, ring.r, ring.width)
return (result + poly(null, shape.points))
}, xmlNode('g', false, {id: id, mask: maskUrl}))
var mask = createElement(
'mask',
{id: maskId, stroke: '#fff'},
[createElement(circleNode.tag, circleNode.attr)])
return mask + group + '</g>'
var groupChildren = shapes.map(function(shape) {
var node = (shape.type === 'rect')
? rect(shape.cx, shape.cy, shape.r, shape.width, shape.height)
: poly(shape.points)
return createElement(node.tag, node.attr)
})
var layer = element('g', {mask: maskUrl}, groupChildren)
return {mask: mask, layer: layer}
}
var reduceShapeArray = function(prefix, code, shapeArray) {
module.exports = function reduceShapeArray(prefix, code, shapeArray, createElement) {
var id = prefix + '_pad-' + code
var maskIdPrefix = id + '_'
var start = ''
var end = ''
if (shapeArray.length > 1) {
start = xmlNode('g', false, {id: id})
end = '</g>'
id = null
}
var image = reduce(shapeArray, function(result, shape) {
var image = shapeArray.reduce(function(result, shape, index) {
var svg

@@ -83,19 +85,22 @@

case 'circle':
svg = circle(id, shape.cx, shape.cy, shape.r)
svg = circle(shape.cx, shape.cy, shape.r)
break
case 'ring':
svg = circle(id, shape.cx, shape.cy, shape.r, shape.width)
svg = circle(shape.cx, shape.cy, shape.r, shape.width)
break
case 'rect':
svg = rect(id, shape.cx, shape.cy, shape.r, shape.width, shape.height)
svg = rect(shape.cx, shape.cy, shape.r, shape.width, shape.height)
break
case 'poly':
svg = poly(id, shape.points)
svg = poly(shape.points)
break
case 'clip':
svg = clip(id, shape.shape, shape.clip)
var clipNodes = clip(maskIdPrefix, index, shape.shape, shape.clip, createElement)
result.masks.push(clipNodes.mask)
svg = clipNodes.layer
break

@@ -106,31 +111,62 @@

result.last = shape.polarity
// if the polarity is clear, wrap the group and start a mask
if (shape.polarity === 'clear') {
var nextMaskId = maskIdPrefix + result.count
result.masks += startMask(nextMaskId, shape.box)
result.layers = maskLayer(nextMaskId, result.layers)
result.maskId = nextMaskId
result.maskBox = shape.box.slice(0)
result.maskChildren = []
result.layers = [maskLayer(nextMaskId, result.layers, createElement)]
}
else {
result.masks += '</mask>'
var mask = createMask(
result.maskId,
result.maskBox,
result.maskChildren,
createElement)
result.masks.push(mask)
}
return result
break
}
if (result.last === 'dark') {
result.layers += svg
if (svg) {
if (shapeArray.length === 1) {
svg.attr.id = id
}
var svgElement = createElement(svg.tag, svg.attr, svg.children)
if (result.last === 'dark') {
result.layers.push(svgElement)
}
else {
result.maskChildren.push(svgElement)
}
}
else {
result.masks += svg
}
return result
}, {count: 0, last: 'dark', layers: '', masks: ''})
}, {
count: 0,
last: 'dark',
layers: [],
maskId: '',
maskBox: [],
maskChildren: [],
masks: []})
if (image.last === 'clear') {
image.masks += '</mask>'
image.masks.push(createMask(
image.maskId,
image.maskBox,
image.maskChildren,
createElement))
}
return image.masks + start + image.layers + end
if (shapeArray.length > 1) {
image.layers = createElement('g', {id: id}, image.layers)
}
return image.masks.concat(image.layers)
}
module.exports = reduceShapeArray
// helper utilities
'use strict'
var reduce = require('lodash.reduce')
// shift the decimal place to SVG coordinates (units * 1000)

@@ -12,20 +10,4 @@ // also round to 7 decimal places

// create an attribute assignment for SVG
var attr = function(attr, val) {
return (attr + '="' + val + '"')
}
// create an open or closed xml node with attributes
var xmlNode = function(name, close, attributes) {
var start = '<' + name
var end = (close) ? '/>' : '>'
var middle = reduce(attributes, function(result, value, key) {
return result + ((value != null) ? (' ' + attr(key, value)) : '')
}, '')
return start + middle + end
}
var boundingRect = function(box, fill) {
return xmlNode('rect', true, {
var boundingRect = function(box, fill, element) {
return element('rect', {
x: shift(box[0]),

@@ -39,13 +21,13 @@ y: shift(box[1]),

var maskLayer = function(maskId, layer) {
var maskLayer = function(maskId, layer, element) {
var maskUrl = 'url(#' + maskId + ')'
return xmlNode('g', false, {mask: maskUrl}) + layer + '</g>'
return element('g', {mask: maskUrl}, layer)
}
var startMask = function(maskId, box) {
var mask = xmlNode('mask', false, {id: maskId, fill: '#000', stroke: '#000'})
mask += boundingRect(box, '#fff')
var createMask = function(maskId, box, children, element) {
children = [boundingRect(box, '#fff', element)].concat(children)
var attributes = {id: maskId, fill: '#000', stroke: '#000'}
return mask
return element('mask', attributes, children)
}

@@ -55,5 +37,4 @@

shift: shift,
xmlNode: xmlNode,
maskLayer: maskLayer,
startMask: startMask
createMask: createMask
}

@@ -5,3 +5,2 @@ // gerber to svg transform stream

var isString = require('lodash.isstring')
var pick = require('lodash.pick')
var gerberParser = require('gerber-parser')

@@ -11,23 +10,36 @@ var gerberPlotter = require('gerber-plotter')

var PlotterToSvg = require('./plotter-to-svg')
var render = require('./_render')
var render = require('./render')
var clone = require('./clone')
var xmlElementString = require('./xml-element-string')
var parseOptions = function(options) {
var optionsIsString = isString(options)
if (options == null || (!optionsIsString && (options.id == null))) {
throw new Error('id required for gerber-to-svg')
var getAttributesFromOptions = function(options) {
if (!options) {
return {}
}
if (optionsIsString) {
return {svg: {id: options.replace('.', '-')}}
var attributes = options.attributes || {}
if (isString(options)) {
attributes.id = options
}
else if (options.id) {
attributes.id = options.id
}
var id = options.id.replace('.', '-')
var className = options.class
var color = options.color
return attributes
}
var parseOptions = function(options) {
var attributes = getAttributesFromOptions(options)
if (!attributes.id) {
throw new Error('Non-empty id required for gerber-to-svg')
}
var opts = {
svg: {
id: id,
class: className,
color: color
attributes: attributes,
createElement: options.createElement || xmlElementString,
includeNamespace: (options.includeNamespace == null) ? true : options.includeNamespace,
objectMode: (options.objectMode == null) ? false : options.objectMode
},

@@ -56,3 +68,8 @@ parser: {

var converter = new PlotterToSvg(opts.svg.id, opts.svg.class, opts.svg.color)
var converter = new PlotterToSvg(
opts.svg.attributes,
opts.svg.createElement,
opts.svg.includeNamespace,
opts.svg.objectMode)
var parser = gerberParser(opts.parser)

@@ -106,2 +123,3 @@ var plotter = gerberPlotter(opts.plotter)

var data
do {

@@ -117,2 +135,3 @@ data = converter.read() || ''

converter.removeListener('end', finishConversion)
return done(error)

@@ -126,5 +145,2 @@ })

module.exports.render = render
module.exports.clone = function cloneConverter(converter) {
return pick(converter, ['defs', 'layer', 'viewBox', 'width', 'height', 'units'])
}
module.exports.clone = clone

@@ -6,5 +6,3 @@ // transform stream to take plotter objects and convert them to an SVG string

var inherits = require('inherits')
var every = require('lodash.every')
var isFinite = require('lodash.isfinite')
var map = require('lodash.map')

@@ -15,8 +13,7 @@ var reduceShapeArray = require('./_reduce-shape')

var util = require('./_util')
var render = require('./_render')
var render = require('./render')
var shift = util.shift
var xmlNode = util.xmlNode
var maskLayer = util.maskLayer
var startMask = util.startMask
var createMask = util.createMask

@@ -27,7 +24,10 @@ var BLOCK_MODE_OFF = 0

var PlotterToSvg = function(id, className, color) {
Transform.call(this, {writableObjectMode: true})
var PlotterToSvg = function(attributes, createElement, includeNamespace, objectMode) {
Transform.call(this, {
writableObjectMode: true,
readableObjectMode: objectMode
})
this.defs = ''
this.layer = ''
this.defs = []
this.layer = []
this.viewBox = [0, 0, 0, 0]

@@ -38,6 +38,8 @@ this.width = 0

this._mask = ''
this._maskId = ''
this._maskBox = []
this._mask = []
this._blockMode = false
this._blockBox = []
this._block = ''
this._block = []
this._blockCount = 0

@@ -49,5 +51,8 @@ this._blockLayerCount = 0

this._blockCount = 0
this._id = id
this._className = className
this._color = color
this._blockCount = 0
this._id = attributes.id
this._attributes = attributes
this._element = createElement
this._includeNamespace = includeNamespace
}

@@ -60,15 +65,20 @@

case 'shape':
this.defs += reduceShapeArray(this._id, chunk.tool, chunk.shape)
this.defs = this.defs.concat(reduceShapeArray(
this._id,
chunk.tool,
chunk.shape,
this._element))
break
case 'pad':
this._draw(flashPad(this._id, chunk.tool, chunk.x, chunk.y))
this._draw(flashPad(this._id, chunk.tool, chunk.x, chunk.y, this._element))
break
case 'fill':
this._draw(createPath(chunk.path))
this._draw(createPath(chunk.path, null, this._element))
break
case 'stroke':
this._draw(createPath(chunk.path, chunk.width))
this._draw(createPath(chunk.path, chunk.width, this._element))
break

@@ -95,4 +105,8 @@

this.push(render(this, this._id, this._className, this._color))
var attributes = this._attributes
var element = this._element
var includeNamespace = this._includeNamespace
this.push(render(this, attributes, element, includeNamespace))
done()

@@ -103,9 +117,10 @@ }

// if there's a block, wrap it up, give it an id, and repeat it
if (this._block) {
if (this._block.length) {
this._blockLayerCount++
var blockLayerId = this._id + '_block-' + this._blockCount + '-' + this._blockLayerCount
this.defs += xmlNode('g', false, {id: blockLayerId}) + this._block + '</g>'
this._block = ''
this.defs.push(this._element('g', {id: blockLayerId}, this._block))
this._block = []
}

@@ -115,5 +130,8 @@ }

PlotterToSvg.prototype._finishClearLayer = function() {
if (this._mask) {
this.defs += this._mask + '</mask>'
this._mask = ''
if (this._maskId) {
this.defs.push(createMask(this._maskId, this._maskBox, this._mask, this._element))
this._maskId = ''
this._maskBox = []
this._mask = []
return true

@@ -127,5 +145,8 @@ }

if (this._blockMode) {
if ((this._blockLayerCount === 0) && (this._block === '')) {
this._blockMode = (polarity === 'dark') ? BLOCK_MODE_DARK : BLOCK_MODE_CLEAR
if ((this._blockLayerCount === 0) && !this._block.length) {
this._blockMode = (polarity === 'dark')
? BLOCK_MODE_DARK
: BLOCK_MODE_CLEAR
}
return this._finishBlockLayer()

@@ -139,4 +160,5 @@ }

if (polarity === 'clear') {
this.layer = maskLayer(maskId, this.layer)
this._mask = startMask(maskId, box)
this.layer = [maskLayer(maskId, this.layer, this._element)]
this._maskId = maskId
this._maskBox = box.slice(0)
}

@@ -154,4 +176,7 @@ // else, finish the mask and add it to the defs

var wasClear = this._finishClearLayer()
this._finishBlockLayer()
var layer = this.layer
var element = this._element
var blockMode = this._blockMode

@@ -162,36 +187,44 @@ var blockLayers = this._blockLayerCount

// add dark layers to layer
this.layer += map(this._offsets, function(offset) {
var result = ''
this._offsets.forEach(function(offset) {
for (var i = blockMode; i <= blockLayers; i += 2) {
result += xmlNode('use', true, {
layer.push(element('use', {
'xlink:href': '#' + blockIdStart + i,
x: shift(offset[0]),
y: shift(offset[1])
})
}))
}
})
return result
}).join('')
// if there are clear layers in the block, mask the layer with them
if (blockLayers > (2 - blockMode)) {
var maskId = blockIdStart + 'clear'
this.layer = maskLayer(maskId, this.layer)
this._mask = startMask(maskId, this._blockBox)
this._mask += map(this._offsets, function(offset) {
var result = ''
this.layer = [maskLayer(maskId, layer, this._element)]
this._maskId = maskId
this._maskBox = this._blockBox.slice(0)
this._mask = this._offsets.reduce(function(result, offset) {
var isDark
for (var i = 1; i <= blockLayers; i++) {
isDark = (blockMode === BLOCK_MODE_DARK) ? ((i % 2) === 1) : ((i % 2) === 0)
result += xmlNode('use', true, {
isDark = (blockMode === BLOCK_MODE_DARK)
? ((i % 2) === 1)
: ((i % 2) === 0)
var attr = {
'xlink:href': '#' + blockIdStart + i,
x: shift(offset[0]),
y: shift(offset[1]),
fill: isDark ? '#fff' : null,
stroke: isDark ? '#fff' : null
})
y: shift(offset[1])
}
if (isDark) {
attr.fill = '#fff',
attr.stroke = '#fff'
}
result.push(element('use', attr))
}
return result
}).join('')
}, [])
wasClear = this._finishClearLayer()

@@ -206,3 +239,3 @@ }

this._blockLayerCount = 0
this._blockBox = every(box, isFinite) ? box : [0, 0, 0, 0]
this._blockBox = box.every(isFinite) ? box : [0, 0, 0, 0]
}

@@ -215,3 +248,3 @@ else {

PlotterToSvg.prototype._handleSize = function(box, units) {
if (every(box, isFinite)) {
if (box.every(isFinite)) {
var x = shift(box[0])

@@ -231,11 +264,11 @@ var y = shift(box[1])

if (!this._blockMode) {
if (!this._mask) {
this.layer += object
if (!this._maskId) {
this.layer.push(object)
}
else {
this._mask += object
this._mask.push(object)
}
}
else {
this._block += object
this._block.push(object)
}

@@ -242,0 +275,0 @@ }

{
"name": "gerber-to-svg",
"version": "1.1.0",
"version": "2.0.0",
"description": "Gerber and NC drill file to SVG converter",

@@ -71,11 +71,8 @@ "main": "lib/gerber-to-svg.js",

"chalk": "^1.1.1",
"escape-html": "^1.0.3",
"gerber-parser": "^1.0.0",
"gerber-plotter": "^1.0.0",
"inherits": "^2.0.1",
"lodash.every": "^4.3.0",
"lodash.isfinite": "^3.2.0",
"lodash.isstring": "^4.0.1",
"lodash.map": "^4.3.0",
"lodash.pick": "^4.2.0",
"lodash.reduce": "^4.3.0",
"minimist": "^1.1.0",

@@ -82,0 +79,0 @@ "mkdirp": "^0.5.1",

@@ -5,12 +5,13 @@ // test suite for gerber-to-svg

var events = require('events')
var proxyquire = require('proxyquire')
var assign = require('lodash.assign')
var sinon = require('sinon')
var chai = require('chai')
var sinonChai = require('sinon-chai')
var proxyquire = require('proxyquire')
var assign = require('lodash.assign')
var expect = chai.expect
var expect = chai.expect
chai.use(sinonChai)
var render = require('../lib/_render')
var render = require('../lib/render')
var clone = require('../lib/clone')

@@ -20,6 +21,8 @@ var parserStub = sinon.stub()

var converterStub = sinon.stub()
var xmlElementSpy = sinon.spy(require('../lib/xml-element-string'))
var gerberToSvg = proxyquire('../lib/gerber-to-svg', {
'gerber-parser': parserStub,
'gerber-plotter': plotterStub,
'./plotter-to-svg': converterStub
'./plotter-to-svg': converterStub,
'./xml-element-string': xmlElementSpy
})

@@ -68,4 +71,5 @@

var converter1 = gerberToSvg('', 'test-id')
var converter2 = gerberToSvg('', 'test-id', function() {})
expect(converter1).to.equal(fakeConverter)
var converter2 = gerberToSvg('', 'test-id', function() {})
expect(converter2).to.equal(fakeConverter)

@@ -98,8 +102,8 @@

var input = {pipe: sinon.spy(), setEncoding: sinon.spy()}
gerberToSvg(input, 'test-id')
expect(input.pipe).to.have.been.calledWith(fakeParser)
expect(fakeParser.pipe).to.have.been.calledWith(fakePlotter)
expect(fakePlotter.pipe).to.have.been.calledWith(fakeConverter)
expect(input.setEncoding).to.have.been.calledWith('utf8')
expect(input.pipe).to.be.calledWith(fakeParser)
expect(fakeParser.pipe).to.be.calledWith(fakePlotter)
expect(fakePlotter.pipe).to.be.calledWith(fakeConverter)
expect(input.setEncoding).to.be.calledWith('utf8')
})

@@ -109,6 +113,7 @@

var input = 'G04 empty gerber*\nM02*\n'
gerberToSvg(input, 'test-id')
setTimeout(function() {
expect(fakeParser.write).to.have.been.calledWith(input)
expect(fakeParser.write).to.be.calledWith(input)
expect(fakeParser.end).to.have.been.calledOnce

@@ -121,3 +126,3 @@ done()

gerberToSvg('', 'foo')
expect(converterStub).to.have.been.calledWith('foo')
expect(converterStub).to.be.calledWith({id: 'foo'})
})

@@ -127,3 +132,3 @@

gerberToSvg('', {id: 'bar'})
expect(converterStub).to.have.been.calledWith('bar')
expect(converterStub).to.be.calledWith({id: 'bar'})
})

@@ -133,19 +138,37 @@

expect(function() {gerberToSvg('', {})}).to.throw(/id required/)
expect(function() {gerberToSvg('')}).to.throw(/id required/)
})
it('should replace dots in the id with dashed', function() {
gerberToSvg('', 'hello.world')
expect(converterStub).to.have.been.calledWith('hello-world')
it('should pass the attributes option', function() {
gerberToSvg('', {id: 'foo', attributes: {bar: 'baz'}})
expect(converterStub).to.be.calledWith({id: 'foo', bar: 'baz'})
})
it('should pass the class option', function() {
gerberToSvg('', {id: 'foo', class: 'bar'})
expect(converterStub).to.have.been.calledWith('foo', 'bar')
it('should pass createElement, which should default to xml-element-string', function() {
var element = function() {}
gerberToSvg('', {id: 'foo'})
expect(converterStub).to.be.calledWith({id: 'foo'}, xmlElementSpy)
gerberToSvg('', {id: 'bar', createElement: element})
expect(converterStub).to.be.calledWith({id: 'bar'}, element)
})
it('should pass the color option', function() {
gerberToSvg('', {id: 'foo', color: 'red'})
expect(converterStub).to.have.been.calledWith('foo', undefined, 'red')
it('should pass includeNamespace, which should default true', function() {
var element = function() {}
gerberToSvg('', {id: 'foo', createElement: element})
expect(converterStub).to.be.calledWith({id: 'foo'}, element, true)
gerberToSvg('', {id: 'bar', createElement: element, includeNamespace: false})
expect(converterStub).to.be.calledWith({id: 'bar'}, element, false)
})
it('should pass objectMode, which should default to false', function() {
var element = function() {}
gerberToSvg('', {id: 'foo', createElement: element})
expect(converterStub).to.be.calledWith({id: 'foo'}, element, true, false)
gerberToSvg('', {id: 'bar', createElement: element, objectMode: true})
expect(converterStub).to.be.calledWith({id: 'bar'}, element, true, true)
})
describe('passing along warnings', function() {

@@ -200,2 +223,3 @@ it('should emit warnings from the parser', function(done) {

var expectedError = {}
gerberToSvg('foobar*\n', 'foobar', function(error) {

@@ -211,2 +235,3 @@ expect(error).to.equal(expectedError)

var expectedError = {}
gerberToSvg('foobar*\n', 'foobar', function(error) {

@@ -223,2 +248,3 @@ expect(error).to.equal(expectedError)

var parser = new events.EventEmitter
assign(parser, fakeParser, {format: {filetype: 'foobar'}})

@@ -228,2 +254,3 @@ parserStub.returns(parser)

var converter = gerberToSvg('G04 a gerber file*\n', 'gbr')
expect(converter.filetype).to.be.falsey

@@ -237,4 +264,4 @@

var fakeConverter = {
defs: 'the',
layer: 'other',
defs: ['the'],
layer: ['other'],
viewBox: [0, 1, 2, 3],

@@ -247,6 +274,7 @@ width: 'I',

expect(String(gerberToSvg.render)).to.equal(String(render))
expect(gerberToSvg.render(fakeConverter)).to.equal(expected)
})
it('shoud have a clone method that clones the public properties of a converter', function() {
it('shoud have a clone method that clones public properties of a converter', function() {
var converter = {

@@ -267,2 +295,3 @@ parser: 'hello',

expect(String(gerberToSvg.clone)).to.equal(String(clone))
expect(gerberToSvg.clone(converter)).to.eql({

@@ -286,5 +315,5 @@ defs: 'the',

}
gerberToSvg('foo*\n', options)
expect(parserStub).to.have.been.calledWith({
expect(parserStub).to.be.calledWith({
places: [2, 3],

@@ -306,5 +335,5 @@ zero: 'T',

}
gerberToSvg('foo*\n', options)
expect(plotterStub).to.have.been.calledWith({
expect(plotterStub).to.be.calledWith({
units: 'in',

@@ -311,0 +340,0 @@ backupUnits: 'mm',

// test suite for the plotter to svg transform stream
'use strict'
var expect = require('chai').expect
var assign = require('lodash.assign')
var sinon = require('sinon')
var chai = require('chai')
var sinonChai = require('sinon-chai')
var expect = chai.expect
chai.use(sinonChai)
var PlotterToSvg = require('../lib/plotter-to-svg')
var xmlElement = require('../lib/xml-element-string')
var HALF_PI = Math.PI / 2
var EMPTY_SVG = [
'<svg id="id" ',
'xmlns="http://www.w3.org/2000/svg" ',
'version="1.1" ',
'xmlns:xlink="http://www.w3.org/1999/xlink" ',
'stroke-linecap="round" ',
'stroke-linejoin="round" ',
'stroke-width="0" ',
'fill-rule="evenodd" ',
'width="0" ',
'height="0" ',
'viewBox="0 0 0 0">',
'</svg>'].join('')
var SVG_ATTR = {
id: 'id',
xmlns: 'http://www.w3.org/2000/svg',
version: '1.1',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '0',
'fill-rule': 'evenodd',
width: '0',
height: '0',
viewBox: '0 0 0 0'
}
var EMPTY_BOX = [Infinity, Infinity, -Infinity, -Infinity]
describe('plotter to svg transform stream', function() {
var p
var element
beforeEach(function() {
p = new PlotterToSvg('id')
element = sinon.spy(xmlElement)
p = new PlotterToSvg({id: 'id'}, element)
p.setEncoding('utf8')

@@ -31,4 +44,4 @@ })

it('should emit an empty svg if it gets a zero size plot', function(done) {
p.once('data', function(result) {
expect(result).to.equal(EMPTY_SVG)
p.once('data', function() {
expect(element).to.be.calledWith('svg', SVG_ATTR, [])
expect(p.viewBox).to.eql([0, 0, 0, 0])

@@ -41,3 +54,3 @@ expect(p.width).to.equal(0)

p.write({type: 'size', box: [Infinity, Infinity, -Infinity, -Infinity], units: ''})
p.write({type: 'size', box: EMPTY_BOX, units: ''})
p.end()

@@ -47,32 +60,35 @@ })

it('should be able to add an id', function(done) {
p = new PlotterToSvg('foo')
p.once('data', function(result) {
expect(result).to.match(/id="foo"/)
var converter = new PlotterToSvg({id: 'foo'}, element)
var expected = assign({}, SVG_ATTR, {id: 'foo'})
converter.once('data', function() {
expect(element).to.be.calledWith('svg', expected)
done()
})
p.write({type: 'size', box: [Infinity, Infinity, -Infinity, -Infinity], units: ''})
p.end()
converter.write({type: 'size', box: EMPTY_BOX, units: ''})
converter.end()
})
it('should be able to add a classname', function(done) {
p = new PlotterToSvg('foo', 'bar')
p.once('data', function(result) {
expect(result).to.match(/class="bar"/)
it('should be able to add other attributes', function(done) {
var converter = new PlotterToSvg({id: 'foo', bar: 'baz'}, element)
var expected = assign({}, SVG_ATTR, {id: 'foo', bar: 'baz'})
converter.once('data', function() {
expect(element).to.be.calledWith('svg', expected)
done()
})
p.write({type: 'size', box: [Infinity, Infinity, -Infinity, -Infinity], units: ''})
p.end()
converter.write({type: 'size', box: EMPTY_BOX, units: ''})
converter.end()
})
it('should be able to add a color', function(done) {
p = new PlotterToSvg('foo', 'bar', 'baz')
p.setEncoding('utf8')
p.once('data', function(result) {
expect(result).to.match(/color="baz"/)
it('should be able to omit the namespace from attributes', function(done) {
p = new PlotterToSvg({id: 'id'}, element, false)
p.once('data', function() {
expect(element.firstCall.args[1].xmlns).to.not.exist
done()
})
p.write({type: 'size', box: [Infinity, Infinity, -Infinity, -Infinity], units: ''})
p.write({type: 'size', box: EMPTY_BOX, units: ''})
p.end()

@@ -84,6 +100,7 @@ })

var toolShape = [{type: 'circle', cx: 0.001, cy: 0.002, r: 0.005}]
var expected = '<circle id="id_pad-10" cx="1" cy="2" r="5"/>'
var expected = {id: 'id_pad-10', cx: 1, cy: 2, r: 5}
p.write({type: 'shape', tool: '10', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('circle', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})

@@ -95,6 +112,7 @@

]
var expected = '<rect id="id_pad-10" x="1" y="2" rx="2" ry="2" width="2" height="4"/>'
var expected = {id: 'id_pad-10', x: 1, y: 2, rx: 2, ry: 2, width: 2, height: 4}
p.write({type: 'shape', tool: '10', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('rect', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})

@@ -104,6 +122,7 @@

var toolShape = [{type: 'poly', points: [[0, 0], [1, 0], [0, 1]]}]
var expected = '<polygon id="id_pad-12" points="0,0 1000,0 0,1000"/>'
var expected = {id: 'id_pad-12', points: '0,0 1000,0 0,1000'}
p.write({type: 'shape', tool: '12', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('polygon', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})

@@ -113,7 +132,12 @@

var toolShape = [{type: 'ring', r: 0.02, width: 0.005, cx: 0.05, cy: -0.03}]
var expected = '<circle id="id_pad-11" cx="50" cy="-30" r="20" '
expected += 'stroke-width="5" fill="none"/>'
var expected = {
id: 'id_pad-11',
cx: 50, cy: -30, r: 20,
'stroke-width': 5,
fill: 'none'
}
p.write({type: 'shape', tool: '11', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('circle', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})

@@ -128,19 +152,24 @@

]
var ring = {type: 'ring', r: 0.004, width: 0.002, cx: 0, cy: 0}
var toolShape = [{type: 'clip', shape: clippedShapes, clip: ring}]
var expected = [
'<mask id="id_pad-15_mask" stroke="#fff">',
'<circle cx="0" cy="0" r="4" stroke-width="2" fill="none"/>',
'</mask>',
'<g id="id_pad-15" mask="url(#id_pad-15_mask)">',
'<rect x="1" y="1" width="4" height="4"/>',
'<rect x="-5" y="1" width="4" height="4"/>',
'<rect x="-5" y="-5" width="4" height="4"/>',
'<rect x="1" y="-5" width="4" height="4"/>',
'</g>'
].join('')
{cx: 0, cy: 0, r: 4, 'stroke-width': 2, fill: 'none'},
{id: 'id_pad-15_mask-0', stroke: '#fff'},
{x: 1, y: 1, width: 4, height: 4},
{x: -5, y: 1, width: 4, height: 4},
{x: -5, y: -5, width: 4, height: 4},
{x: 1, y: -5, width: 4, height: 4},
{id: 'id_pad-15', mask: 'url(#id_pad-15_mask-0)'}
]
p.write({type: 'shape', tool: '15', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('circle', expected[0])
expect(element).to.be.calledWith('mask', expected[1], [element.returnValues[0]])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('rect', expected[4])
expect(element).to.be.calledWith('rect', expected[5])
expect(element).to.be.calledWith('g', expected[6], element.returnValues.slice(2, 6))
expect(p.defs).to.eql([element.returnValues[1], element.returnValues[6]])
})

@@ -162,17 +191,21 @@

var toolShape = [{type: 'clip', shape: clippedShapes, clip: ring}]
var expected = [
'<mask id="id_pad-15_mask" stroke="#fff">',
'<circle cx="0" cy="0" r="4" stroke-width="2" fill="none"/>',
'</mask>',
'<g id="id_pad-15" mask="url(#id_pad-15_mask)">',
'<polygon points="1,1 5,1 5,5 1,5"/>',
'<polygon points="-5,1 -1,1 -1,5 -5,5"/>',
'<polygon points="-5,-5 -1,-5 -1,-1 -5,-1"/>',
'<polygon points="1,-5 5,-5 5,-1 1,-1"/>',
'</g>'
].join('')
{cx: 0, cy: 0, r: 4, 'stroke-width': 2, fill: 'none'},
{id: 'id_pad-15_mask-0', stroke: '#fff'},
{points: '1,1 5,1 5,5 1,5'},
{points: '-5,1 -1,1 -1,5 -5,5'},
{points: '-5,-5 -1,-5 -1,-1 -5,-1'},
{points: '1,-5 5,-5 5,-1 1,-1'},
{id: 'id_pad-15', mask: 'url(#id_pad-15_mask-0)'}
]
p.write({type: 'shape', tool: '15', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('circle', expected[0])
expect(element).to.be.calledWith('mask', expected[1], [element.returnValues[0]])
expect(element).to.be.calledWith('polygon', expected[2])
expect(element).to.be.calledWith('polygon', expected[3])
expect(element).to.be.calledWith('polygon', expected[4])
expect(element).to.be.calledWith('polygon', expected[5])
expect(element).to.be.calledWith('g', expected[6], element.returnValues.slice(2, 6))
expect(p.defs).to.eql([element.returnValues[1], element.returnValues[6]])
})

@@ -189,14 +222,79 @@

var expected = [
'<g id="id_pad-20">',
'<rect x="1" y="1" width="4" height="4"/>',
'<rect x="-5" y="1" width="4" height="4"/>',
'<rect x="-5" y="-5" width="4" height="4"/>',
'<rect x="1" y="-5" width="4" height="4"/>',
'</g>'
].join('')
{x: 1, y: 1, width: 4, height: 4},
{x: -5, y: 1, width: 4, height: 4},
{x: -5, y: -5, width: 4, height: 4},
{x: 1, y: -5, width: 4, height: 4},
{id: 'id_pad-20'}
]
p.write({type: 'shape', tool: '20', shape: toolShape})
expect(p.defs).to.equal(expected)
expect(element).to.be.calledWith('rect', expected[0])
expect(element).to.be.calledWith('rect', expected[1])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('g', expected[4], element.returnValues.slice(0, 4))
expect(p.defs).to.eql([element.returnValues[4]])
})
it('should handle multiple clipped primitives', function() {
var clippedShapes1 = [
{type: 'rect', cx: 0.003, cy: 0.003, width: 0.004, height: 0.004, r: 0},
{type: 'rect', cx: -0.003, cy: 0.003, width: 0.004, height: 0.004, r: 0},
{type: 'rect', cx: -0.003, cy: -0.003, width: 0.004, height: 0.004, r: 0},
{type: 'rect', cx: 0.003, cy: -0.003, width: 0.004, height: 0.004, r: 0}
]
var clippedShapes2 = [
{type: 'rect', cx: 0.003, cy: 0.003, width: 0.002, height: 0.002, r: 0},
{type: 'rect', cx: -0.003, cy: 0.003, width: 0.002, height: 0.002, r: 0},
{type: 'rect', cx: -0.003, cy: -0.003, width: 0.002, height: 0.002, r: 0},
{type: 'rect', cx: 0.003, cy: -0.003, width: 0.002, height: 0.002, r: 0}
]
var ring1 = {type: 'ring', r: 0.004, width: 0.002, cx: 0, cy: 0}
var ring2 = {type: 'ring', r: 0.002, width: 0.001, cx: 0, cy: 0}
var toolShape = [
{type: 'clip', shape: clippedShapes1, clip: ring1},
{type: 'clip', shape: clippedShapes2, clip: ring2}
]
p.write({type: 'shape', tool: '15', shape: toolShape})
var values = element.returnValues
var expected = [
{cx: 0, cy: 0, r: 4, 'stroke-width': 2, fill: 'none'},
{id: 'id_pad-15_mask-0', stroke: '#fff'},
{x: 1, y: 1, width: 4, height: 4},
{x: -5, y: 1, width: 4, height: 4},
{x: -5, y: -5, width: 4, height: 4},
{x: 1, y: -5, width: 4, height: 4},
{mask: 'url(#id_pad-15_mask-0)'},
{cx: 0, cy: 0, r: 2, 'stroke-width': 1, fill: 'none'},
{id: 'id_pad-15_mask-1', stroke: '#fff'},
{x: 2, y: 2, width: 2, height: 2},
{x: -4, y: 2, width: 2, height: 2},
{x: -4, y: -4, width: 2, height: 2},
{x: 2, y: -4, width: 2, height: 2},
{mask: 'url(#id_pad-15_mask-1)'},
{id: 'id_pad-15'}
]
expect(element).to.be.calledWith('circle', expected[0])
expect(element).to.be.calledWith('mask', expected[1], [values[0]])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('rect', expected[4])
expect(element).to.be.calledWith('rect', expected[5])
expect(element).to.be.calledWith('g', expected[6], values.slice(2, 6))
expect(element).to.be.calledWith('circle', expected[7])
expect(element).to.be.calledWith('mask', expected[8], [values[7]])
expect(element).to.be.calledWith('rect', expected[9])
expect(element).to.be.calledWith('rect', expected[10])
expect(element).to.be.calledWith('rect', expected[11])
expect(element).to.be.calledWith('rect', expected[12])
expect(element).to.be.calledWith('g', expected[13], values.slice(9, 13))
expect(element).to.be.calledWith('g', expected[14], [values[6], values[13]])
expect(p.defs).to.eql([values[1], values[8], values[14]])
})
it('should handle polarity changes', function() {

@@ -216,24 +314,35 @@ var toolShape = [

var expected = [
'<mask id="id_pad-11_1" fill="#000" stroke="#000">',
'<rect x="-3" y="1" width="6" height="8" fill="#fff"/>',
'<rect x="-2" y="3" width="4" height="4"/>',
'</mask>',
'<mask id="id_pad-11_3" fill="#000" stroke="#000">',
'<rect x="-4" y="-9" width="8" height="13" fill="#fff"/>',
'<rect x="-2" y="-7" width="4" height="4"/>',
'<circle cx="0" cy="0" r="2"/>',
'</mask>',
'<g id="id_pad-11">',
'<g mask="url(#id_pad-11_3)">',
'<g mask="url(#id_pad-11_1)">',
'<rect x="-3" y="1" width="6" height="8"/>',
'</g>',
'<rect x="-3" y="-9" width="6" height="8"/>',
'<circle cx="0" cy="0" r="4"/>',
'</g>',
'</g>'
].join('')
{x: -3, y: 1, width: 6, height: 8},
{mask: 'url(#id_pad-11_1)'},
{x: -2, y: 3, width: 4, height: 4},
{x: -3, y: 1, width: 6, height: 8, fill: '#fff'},
{id: 'id_pad-11_1', fill: '#000', stroke: '#000'},
{x: -3, y: -9, width: 6, height: 8},
{cx: 0, cy: 0, r: 4},
{mask: 'url(#id_pad-11_3)'},
{x: -2, y: -7, width: 4, height: 4},
{cx: 0, cy: 0, r: 2},
{x: -4, y: -9, width: 8, height: 13, fill: '#fff'},
{id: 'id_pad-11_3', fill: '#000', stroke: '#000'},
{id: 'id_pad-11'}
]
p.write({type: 'shape', tool: '11', shape: toolShape})
expect(p.defs).to.equal(expected)
var values = element.returnValues
expect(element).to.be.calledWith('rect', expected[0])
expect(element).to.be.calledWith('g', expected[1], [values[0]])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('mask', expected[4], [values[3], values[2]])
expect(element).to.be.calledWith('rect', expected[5])
expect(element).to.be.calledWith('circle', expected[6])
expect(element).to.be.calledWith('g', expected[7], [values[1], values[5], values[6]])
expect(element).to.be.calledWith('rect', expected[8])
expect(element).to.be.calledWith('circle', expected[9])
expect(element).to.be.calledWith('rect', expected[10])
expect(element).to.be.calledWith('mask', expected[11], [values[10], values[8], values[9]])
expect(element).to.be.calledWith('g', expected[12], [values[7]])
expect(p.defs).to.eql([values[4], values[11], values[12]])
})

@@ -244,6 +353,6 @@ })

var pad = {type: 'pad', tool: '24', x: 0.020, y: 0.050}
var expected = '<use xlink:href="#id_pad-24" x="20" y="50"/>'
p.write(pad)
expect(p.layer).to.equal(expected)
expect(element).to.be.calledWith('use', {'xlink:href': '#id_pad-24', x: 20, y: 50})
expect(p.layer).to.eql(element.returnValues)
})

@@ -254,6 +363,6 @@

var fill = {type: 'fill', path: []}
var expected = '<path d=""/>'
p.write(fill)
expect(p.layer).to.equal(expected)
expect(element).to.be.calledWith('path', {d: ''})
expect(p.layer).to.eql(element.returnValues)
})

@@ -263,6 +372,6 @@

var stroke = {type: 'stroke', path: [], width: 0.006}
var expected = '<path d="" fill="none" stroke-width="6"/>'
p.write(stroke)
expect(p.layer).to.equal(expected)
expect(element).to.be.calledWith('path', {d: '', fill: 'none', 'stroke-width': 6})
expect(p.layer).to.eql(element.returnValues)
})

@@ -278,6 +387,7 @@

var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = 'd="M 0 0 100 0 100 100 0 100 0 0"'
var expected = {d: 'M 0 0 100 0 100 100 0 100 0 0', fill: 'none', 'stroke-width': 6}
p.write(stroke)
expect(p.layer).to.include(expectedData)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})

@@ -292,6 +402,11 @@

var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = 'd="M 0 0 100 100 M 200 200 300 300 M 400 400 500 500"'
var expected = {
d: 'M 0 0 100 100 M 200 200 300 300 M 400 400 500 500',
fill: 'none',
'stroke-width': 6
}
p.write(stroke)
expect(p.layer).to.include(expectedData)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})

@@ -324,7 +439,12 @@

var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = 'd="M 100 0 A 100 100 0 0 1 0 100 100 100 0 1 1 100 0 '
expectedData += 'M 1100 0 A 100 100 0 1 0 1000 100 100 100 0 0 0 1100 0'
var expectedData = [
'M 100 0 A 100 100 0 0 1 0 100 100 100 0 1 1 100 0',
'M 1100 0 A 100 100 0 1 0 1000 100 100 100 0 0 0 1100 0'
].join(' ')
var expected = {d: expectedData, fill: 'none', 'stroke-width': 6}
p.write(stroke)
expect(p.layer).to.include(expectedData)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})

@@ -340,6 +460,7 @@

var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = 'd="M 0 0 0 0"'
var expected = {d: 'M 0 0 0 0', fill: 'none', 'stroke-width': 6}
p.write(stroke)
expect(p.layer).to.include(expectedData)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})

@@ -355,6 +476,11 @@

var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = 'd="M 0 0 A 100 100 0 0 1 -200 0 100 100 0 0 1 0 0"'
var expected = {
d: 'M 0 0 A 100 100 0 0 1 -200 0 100 100 0 0 1 0 0',
fill: 'none',
'stroke-width': 6
}
p.write(stroke)
expect(p.layer).to.include(expectedData)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})

@@ -373,6 +499,11 @@

var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = 'd="M 100 0 A 100 100 0 0 1 -100 0 L 0 0"'
var expected = {
d: 'M 100 0 A 100 100 0 0 1 -100 0 L 0 0',
fill: 'none',
'stroke-width': 6
}
p.write(stroke)
expect(p.layer).to.include(expectedData)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})

@@ -383,49 +514,41 @@ })

it('should wrap the layer in a masked group when polarity becomes clear', function() {
p.layer = '<path d="M 0 0 1 0 1 1 0 1 0 0"/>'
var existing = ['<path d="M 0 0 1 0 1 1 0 1 0 0"/>']
var polarity = {type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]}
var expected = '<g mask="url(#id_clear-1)"><path d="M 0 0 1 0 1 1 0 1 0 0"/></g>'
p.layer = existing.slice(0)
p.write(polarity)
expect(p.layer).to.equal(expected)
expect(element).to.be.calledWith('g', {mask: 'url(#id_clear-1)'}, existing)
expect(p.layer).to.eql(element.returnValues)
})
it('should start a mask when polarity becomes clear', function() {
var polarity = {type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]}
var expected = '<mask id="id_clear-1" fill="#000" stroke="#000">'
expected += '<rect x="0" y="0" width="1000" height="1000" fill="#fff"/>'
it('should construct a mask and add to defs when polarity switches back', function() {
var clear = {type: 'polarity', polarity: 'clear', box: [0, 0, 0.5, 0.5]}
var clearPad = {type: 'pad', tool: '10', x: 0.005, y: 0.005}
var dark = {type: 'polarity', polarity: 'dark', box: [0, 0, 0.5, 0.5]}
var darkPad = {type: 'pad', tool: '11', x: 0.005, y: 0.005}
p.write(polarity)
expect(p._mask).to.equal(expected)
})
p.write(clear)
p.write(clearPad)
p.write(dark)
p.write(darkPad)
it('should write new shapes to the mask when polarity is clear', function() {
var polarity = {type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]}
var pad = {type: 'pad', tool: '10', x: 0.005, y: 0.005}
var expected = '<mask id="id_clear-1" fill="#000" stroke="#000">'
expected += '<rect x="0" y="0" width="1000" height="1000" fill="#fff"/>'
expected += '<use xlink:href="#id_pad-10" x="5" y="5"/>'
var values = element.returnValues
var expected = [
{mask: 'url(#id_clear-1)'},
{'xlink:href': '#id_pad-10', x: 5, y: 5},
{x: 0, y: 0, width: 500, height: 500, fill: '#fff'},
{id: 'id_clear-1', fill: '#000', stroke: '#000'},
{'xlink:href': '#id_pad-11', x: 5, y: 5}
]
p.write(polarity)
p.write(pad)
expect(p.layer).to.equal('<g mask="url(#id_clear-1)"></g>')
expect(p._mask).to.equal(expected)
expect(element).to.be.calledWith('g', expected[0], [])
expect(element).to.be.calledWith('use', expected[1])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('mask', expected[3], [values[2], values[1]])
expect(element).to.be.calledWith('use', expected[4])
expect(p._mask).to.eql([])
expect(p.defs).to.eql([values[3]])
expect(p.layer).to.eql([values[0], values[4]])
})
it('should finish the mask and add to defs when polarity switches back', function() {
var clear = {type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]}
var dark = {type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]}
var pad = {type: 'pad', tool: '10', x: 0.005, y: 0.005}
var expectedDefs = '<mask id="id_clear-1" fill="#000" stroke="#000">'
expectedDefs += '<rect x="0" y="0" width="1000" height="1000" fill="#fff"/></mask>'
var expectedLayer = '<g mask="url(#id_clear-1)"></g>'
expectedLayer += '<use xlink:href="#id_pad-10" x="5" y="5"/>'
p.write(clear)
p.write(dark)
p.write(pad)
expect(p._mask).to.equal('')
expect(p.defs).to.equal(expectedDefs)
expect(p.layer).to.equal(expectedLayer)
})
it('should not do anything with dark polarity if there is no mask', function() {

@@ -435,5 +558,6 @@ var dark = {type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]}

p.write(dark)
expect(p._mask).to.equal('')
expect(p.defs).to.equal('')
expect(p.layer).to.equal('')
expect(element).to.not.be.called
expect(p._mask).to.eql([])
expect(p.defs).to.eql([])
expect(p.layer).to.eql([])
})

@@ -445,14 +569,22 @@ })

var offsets = [[0, 0], [0, 1], [1, 0], [1, 1]]
var expected = [
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-1', x: 0, y: 1000},
{'xlink:href': '#id_block-1-1', x: 1000, y: 0},
{'xlink:href': '#id_block-1-1', x: 1000, y: 1000}
]
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 0.5, 0.5]})
p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})
p.write({type: 'repeat', offsets: [], box: [0, 0, 1.5, 1.5]})
expect(p.defs).to.equal(
'<g id="id_block-1-1"><use xlink:href="#id_pad-10" x="250" y="250"/></g>')
expect(p.layer).to.equal([
'<use xlink:href="#id_block-1-1" x="0" y="0"/>',
'<use xlink:href="#id_block-1-1" x="0" y="1000"/>',
'<use xlink:href="#id_block-1-1" x="1000" y="0"/>',
'<use xlink:href="#id_block-1-1" x="1000" y="1000"/>'
].join(''))
expect(element).to.be.calledWith('use', expected[0])
expect(element).to.be.calledWith('g', expected[1], [element.returnValues[0]])
expect(element).to.be.calledWith('use', expected[2])
expect(element).to.be.calledWith('use', expected[3])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('use', expected[5])
expect(p.defs).to.eql([element.returnValues[1]])
expect(p.layer).to.eql(element.returnValues.slice(2, 6))
})

@@ -462,2 +594,3 @@

var offsets = [[0, 0], [0, 5], [5, 0], [5, 5]]
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 0.5, 0.5]})

@@ -475,48 +608,110 @@ p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})

expect(p.defs).to.equal([
'<g id="id_block-1-1"><use xlink:href="#id_pad-10" x="250" y="250"/></g>',
'<g id="id_block-1-2"><use xlink:href="#id_pad-11" x="500" y="500"/></g>',
'<g id="id_block-1-3"><use xlink:href="#id_pad-12" x="750" y="750"/></g>',
'<g id="id_block-1-4"><use xlink:href="#id_pad-13" x="1000" y="1000"/></g>',
'<g id="id_block-1-5"><use xlink:href="#id_pad-14" x="1250" y="1250"/></g>',
'<mask id="id_block-1-clear" fill="#000" stroke="#000">',
'<rect x="0" y="0" width="500" height="500" fill="#fff"/>',
'<use xlink:href="#id_block-1-1" x="0" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-2" x="0" y="0"/>',
'<use xlink:href="#id_block-1-3" x="0" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-4" x="0" y="0"/>',
'<use xlink:href="#id_block-1-5" x="0" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-1" x="0" y="5000" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-2" x="0" y="5000"/>',
'<use xlink:href="#id_block-1-3" x="0" y="5000" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-4" x="0" y="5000"/>',
'<use xlink:href="#id_block-1-5" x="0" y="5000" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-1" x="5000" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-2" x="5000" y="0"/>',
'<use xlink:href="#id_block-1-3" x="5000" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-4" x="5000" y="0"/>',
'<use xlink:href="#id_block-1-5" x="5000" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-1" x="5000" y="5000" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-2" x="5000" y="5000"/>',
'<use xlink:href="#id_block-1-3" x="5000" y="5000" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-4" x="5000" y="5000"/>',
'<use xlink:href="#id_block-1-5" x="5000" y="5000" fill="#fff" stroke="#fff"/>',
'</mask>'
].join(''))
expect(p.layer).to.equal([
'<g mask="url(#id_block-1-clear)">',
'<use xlink:href="#id_block-1-1" x="0" y="0"/>',
'<use xlink:href="#id_block-1-3" x="0" y="0"/>',
'<use xlink:href="#id_block-1-5" x="0" y="0"/>',
'<use xlink:href="#id_block-1-1" x="0" y="5000"/>',
'<use xlink:href="#id_block-1-3" x="0" y="5000"/>',
'<use xlink:href="#id_block-1-5" x="0" y="5000"/>',
'<use xlink:href="#id_block-1-1" x="5000" y="0"/>',
'<use xlink:href="#id_block-1-3" x="5000" y="0"/>',
'<use xlink:href="#id_block-1-5" x="5000" y="0"/>',
'<use xlink:href="#id_block-1-1" x="5000" y="5000"/>',
'<use xlink:href="#id_block-1-3" x="5000" y="5000"/>',
'<use xlink:href="#id_block-1-5" x="5000" y="5000"/>',
'</g>'
].join(''))
var values = element.returnValues
var expected = [
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{'xlink:href': '#id_pad-11', x: 500, y: 500},
{id: 'id_block-1-2'},
{'xlink:href': '#id_pad-12', x: 750, y: 750},
{id: 'id_block-1-3'},
{'xlink:href': '#id_pad-13', x: 1000, y: 1000},
{id: 'id_block-1-4'},
{'xlink:href': '#id_pad-14', x: 1250, y: 1250},
{id: 'id_block-1-5'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-3', x: 0, y: 0},
{'xlink:href': '#id_block-1-5', x: 0, y: 0},
{'xlink:href': '#id_block-1-1', x: 0, y: 5000},
{'xlink:href': '#id_block-1-3', x: 0, y: 5000},
{'xlink:href': '#id_block-1-5', x: 0, y: 5000},
{'xlink:href': '#id_block-1-1', x: 5000, y: 0},
{'xlink:href': '#id_block-1-3', x: 5000, y: 0},
{'xlink:href': '#id_block-1-5', x: 5000, y: 0},
{'xlink:href': '#id_block-1-1', x: 5000, y: 5000},
{'xlink:href': '#id_block-1-3', x: 5000, y: 5000},
{'xlink:href': '#id_block-1-5', x: 5000, y: 5000},
{mask: 'url(#id_block-1-clear)'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-2', x: 0, y: 0},
{'xlink:href': '#id_block-1-3', x: 0, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-4', x: 0, y: 0},
{'xlink:href': '#id_block-1-5', x: 0, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-1', x: 0, y: 5000, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-2', x: 0, y: 5000},
{'xlink:href': '#id_block-1-3', x: 0, y: 5000, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-4', x: 0, y: 5000},
{'xlink:href': '#id_block-1-5', x: 0, y: 5000, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-1', x: 5000, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-2', x: 5000, y: 0},
{'xlink:href': '#id_block-1-3', x: 5000, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-4', x: 5000, y: 0},
{'xlink:href': '#id_block-1-5', x: 5000, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-1', x: 5000, y: 5000, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-2', x: 5000, y: 5000},
{'xlink:href': '#id_block-1-3', x: 5000, y: 5000, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-4', x: 5000, y: 5000},
{'xlink:href': '#id_block-1-5', x: 5000, y: 5000, fill: '#fff', stroke: '#fff'},
{x: 0, y: 0, width: 500, height: 500, fill: '#fff'},
{id: 'id_block-1-clear', fill: '#000', stroke: '#000'}
]
expect(element).to.be.calledWith('use', expected[0])
expect(element).to.be.calledWith('g', expected[1], [values[0]])
expect(element).to.be.calledWith('use', expected[2])
expect(element).to.be.calledWith('g', expected[3], [values[2]])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('g', expected[5], [values[4]])
expect(element).to.be.calledWith('use', expected[6])
expect(element).to.be.calledWith('g', expected[7], [values[6]])
expect(element).to.be.calledWith('use', expected[8])
expect(element).to.be.calledWith('g', expected[9], [values[8]])
expect(element).to.be.calledWith('use', expected[10])
expect(element).to.be.calledWith('use', expected[11])
expect(element).to.be.calledWith('use', expected[12])
expect(element).to.be.calledWith('use', expected[13])
expect(element).to.be.calledWith('use', expected[14])
expect(element).to.be.calledWith('use', expected[15])
expect(element).to.be.calledWith('use', expected[16])
expect(element).to.be.calledWith('use', expected[17])
expect(element).to.be.calledWith('use', expected[18])
expect(element).to.be.calledWith('use', expected[19])
expect(element).to.be.calledWith('use', expected[20])
expect(element).to.be.calledWith('use', expected[21])
expect(element).to.be.calledWith('g', expected[22], values.slice(10, 22))
expect(element).to.be.calledWith('use', expected[23])
expect(element).to.be.calledWith('use', expected[24])
expect(element).to.be.calledWith('use', expected[25])
expect(element).to.be.calledWith('use', expected[26])
expect(element).to.be.calledWith('use', expected[27])
expect(element).to.be.calledWith('use', expected[28])
expect(element).to.be.calledWith('use', expected[29])
expect(element).to.be.calledWith('use', expected[30])
expect(element).to.be.calledWith('use', expected[31])
expect(element).to.be.calledWith('use', expected[32])
expect(element).to.be.calledWith('use', expected[33])
expect(element).to.be.calledWith('use', expected[34])
expect(element).to.be.calledWith('use', expected[35])
expect(element).to.be.calledWith('use', expected[36])
expect(element).to.be.calledWith('use', expected[37])
expect(element).to.be.calledWith('use', expected[38])
expect(element).to.be.calledWith('use', expected[39])
expect(element).to.be.calledWith('use', expected[40])
expect(element).to.be.calledWith('use', expected[41])
expect(element).to.be.calledWith('use', expected[42])
expect(element).to.be.calledWith('rect', expected[43])
expect(element).to.be.calledWith(
'mask',
expected[44],
[values[43]].concat(values.slice(23, 43)))
expect(p.defs).to.eql([
values[1],
values[3],
values[5],
values[7],
values[9],
values[44]
])
expect(p.layer).to.eql([values[22]])
})

@@ -526,6 +721,6 @@

var offsets = [[0, 0], [0, 0.5], [0.5, 0], [0.5, 0.5]]
p.layer = 'SOME_EXISTING_STUFF'
p.layer = ['LAYER']
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p._mask += 'SOME_EXISTING_CLEAR_STUFF'
p._mask = ['MASK']
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 1, 1]})

@@ -537,32 +732,59 @@ p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})

expect(p.defs).to.equal([
'<mask id="id_clear-1" fill="#000" stroke="#000">',
'<rect x="0" y="0" width="1000" height="1000" fill="#fff"/>',
'SOME_EXISTING_CLEAR_STUFF',
'</mask>',
'<g id="id_block-1-1"><use xlink:href="#id_pad-10" x="250" y="250"/></g>',
'<g id="id_block-1-2"><use xlink:href="#id_pad-11" x="250" y="250"/></g>',
'<mask id="id_block-1-clear" fill="#000" stroke="#000">',
'<rect x="0" y="0" width="1000" height="1000" fill="#fff"/>',
'<use xlink:href="#id_block-1-1" x="0" y="0"/>',
'<use xlink:href="#id_block-1-2" x="0" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-1" x="0" y="500"/>',
'<use xlink:href="#id_block-1-2" x="0" y="500" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-1" x="500" y="0"/>',
'<use xlink:href="#id_block-1-2" x="500" y="0" fill="#fff" stroke="#fff"/>',
'<use xlink:href="#id_block-1-1" x="500" y="500"/>',
'<use xlink:href="#id_block-1-2" x="500" y="500" fill="#fff" stroke="#fff"/>',
'</mask>'
].join(''))
expect(p.layer).to.equal([
'<g mask="url(#id_block-1-clear)">',
'<g mask="url(#id_clear-1)">',
'SOME_EXISTING_STUFF',
'</g>',
'<use xlink:href="#id_block-1-2" x="0" y="0"/>',
'<use xlink:href="#id_block-1-2" x="0" y="500"/>',
'<use xlink:href="#id_block-1-2" x="500" y="0"/>',
'<use xlink:href="#id_block-1-2" x="500" y="500"/>',
'</g>'
].join(''))
var values = element.returnValues
var expected = [
{mask: 'url(#id_block-1-clear)'},
{x: 0, y: 0, width: 1000, height: 1000, fill: '#fff'},
{id: 'id_clear-1', fill: '#000', stroke: '#000'},
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{'xlink:href': '#id_pad-11', x: 250, y: 250},
{id: 'id_block-1-2'},
{'xlink:href': '#id_block-1-2', x: 0, y: 0},
{'xlink:href': '#id_block-1-2', x: 0, y: 500},
{'xlink:href': '#id_block-1-2', x: 500, y: 0},
{'xlink:href': '#id_block-1-2', x: 500, y: 500},
{mask: 'url(#id_block-1-clear)'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-2', x: 0, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-1', x: 0, y: 500},
{'xlink:href': '#id_block-1-2', x: 0, y: 500, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-1', x: 500, y: 0},
{'xlink:href': '#id_block-1-2', x: 500, y: 0, fill: '#fff', stroke: '#fff'},
{'xlink:href': '#id_block-1-1', x: 500, y: 500},
{'xlink:href': '#id_block-1-2', x: 500, y: 500, fill: '#fff', stroke: '#fff'},
{x: 0, y: 0, width: 1000, height: 1000, fill: '#fff'},
{id: 'id_block-1-clear', fill: '#000', stroke: '#000'}
]
expect(element).to.be.calledWith('g', expected[0])
expect(element).to.be.calledWith('rect', expected[1])
expect(element).to.be.calledWith('mask', expected[2], [values[1], 'MASK'])
expect(element).to.be.calledWith('use', expected[3])
expect(element).to.be.calledWith('g', expected[4], [values[3]])
expect(element).to.be.calledWith('use', expected[5])
expect(element).to.be.calledWith('g', expected[6], [values[5]])
expect(element).to.be.calledWith('use', expected[7])
expect(element).to.be.calledWith('use', expected[8])
expect(element).to.be.calledWith('use', expected[9])
expect(element).to.be.calledWith('use', expected[10])
expect(element).to.be.calledWith(
'g',
expected[11],
[values[0]].concat(values.slice(7, 11)))
expect(element).to.be.calledWith('use', expected[12])
expect(element).to.be.calledWith('use', expected[13])
expect(element).to.be.calledWith('use', expected[14])
expect(element).to.be.calledWith('use', expected[15])
expect(element).to.be.calledWith('use', expected[16])
expect(element).to.be.calledWith('use', expected[17])
expect(element).to.be.calledWith('use', expected[18])
expect(element).to.be.calledWith('use', expected[19])
expect(element).to.be.calledWith('rect', expected[20])
expect(element).to.be.calledWith(
'mask',
expected[21],
[values[20]].concat(values.slice(12, 20)))
expect(p.defs).to.eql([values[2], values[4], values[6], values[21]])
expect(p.layer).to.eql([values[11]])
})

@@ -572,3 +794,4 @@

var offsets = [[0, 0], [0, 0.5], [0.5, 0], [0.5, 0.5]]
p.layer = 'SOME_EXISTING_STUFF'
p.layer = ['SOME_EXISTING_STUFF']
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 1, 1]})

@@ -579,18 +802,48 @@ p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})

expect(p.defs).to.equal([
'<g id="id_block-1-1"><use xlink:href="#id_pad-10" x="250" y="250"/></g>',
'<mask id="id_block-1-clear" fill="#000" stroke="#000">',
'<rect x="0" y="0" width="1000" height="1000" fill="#fff"/>',
'<use xlink:href="#id_block-1-1" x="0" y="0"/>',
'<use xlink:href="#id_block-1-1" x="0" y="500"/>',
'<use xlink:href="#id_block-1-1" x="500" y="0"/>',
'<use xlink:href="#id_block-1-1" x="500" y="500"/>',
'</mask>'
].join(''))
expect(p.layer).to.equal([
'<g mask="url(#id_block-1-clear)">',
'SOME_EXISTING_STUFF',
'</g>'
].join(''))
var values = element.returnValues
var expected = [
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{mask: 'url(#id_block-1-clear)'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-1', x: 0, y: 500},
{'xlink:href': '#id_block-1-1', x: 500, y: 0},
{'xlink:href': '#id_block-1-1', x: 500, y: 500},
{x: 0, y: 0, width: 1000, height: 1000, fill: '#fff'},
{id: 'id_block-1-clear', fill: '#000', stroke: '#000'}
]
expect(element).to.be.calledWith('use', expected[0])
expect(element).to.be.calledWith('g', expected[1], [values[0]])
expect(element).to.be.calledWith('g', expected[2], ['SOME_EXISTING_STUFF'])
expect(element).to.be.calledWith('use', expected[3])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('use', expected[5])
expect(element).to.be.calledWith('use', expected[6])
expect(element).to.be.calledWith('rect', expected[7])
expect(element).to.be.calledWith('mask', expected[8], [values[7]].concat(values.slice(3, 7)))
expect(p.defs).to.eql([values[1], values[8]])
expect(p.layer).to.eql([values[2]])
})
it('should handle polarity switches with no objects gracefully', function() {
var offsets = [[0, 0], [0, 0.5], [0.5, 0], [0.5, 0.5]]
p.layer = ['SOME_EXISTING_STUFF']
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]})
expect(element).to.not.be.called
})
it('should handle Infinities in the box', function() {
var offsets = [[0, 0], [0, 0.5], [0.5, 0], [0.5, 0.5]]
p.layer = ['SOME_EXISTING_STUFF']
p.write({type: 'repeat', offsets: offsets, box: EMPTY_BOX})
expect(p._blockBox).to.eql([0, 0, 0, 0])
})
})

@@ -611,21 +864,17 @@

var size = {type: 'size', box: [-1, -1, 1, 2], units: 'mm'}
var expected = [
'<svg id="id" xmlns="http://www.w3.org/2000/svg" version="1.1" ',
'xmlns:xlink="http://www.w3.org/1999/xlink" ',
'stroke-linecap="round" stroke-linejoin="round" stroke-width="0" ',
'fill-rule="evenodd" ',
'width="2mm" height="3mm" viewBox="-1000 -1000 2000 3000">',
'<defs>THESE_ARE_THE_DEFS</defs>',
'<g transform="translate(0,1000) scale(1,-1)" ',
'fill="currentColor" stroke="currentColor">THIS_IS_THE_clear</g>',
'</svg>'
].join('')
var viewBox = '-1000 -1000 2000 3000'
var transform = 'translate(0,1000) scale(1,-1)'
var svgAttr = assign({}, SVG_ATTR, {width: '2mm', height: '3mm', viewBox: viewBox})
var layerAttr = {transform: transform, fill: 'currentColor', stroke: 'currentColor'}
p.on('data', function(result) {
expect(result).to.equal(expected)
expect(element).to.be.calledWith('defs', {}, ['THESE_ARE_THE_DEFS'])
expect(element).to.be.calledWith('g', layerAttr, ['THIS_IS_THE_LAYER'])
expect(element).to.be.calledWith('svg', svgAttr, element.returnValues.slice(0, 2))
expect(result).to.equal(element.returnValues[2])
done()
})
p.defs = 'THESE_ARE_THE_DEFS'
p.layer = 'THIS_IS_THE_clear'
p.defs = ['THESE_ARE_THE_DEFS']
p.layer = ['THIS_IS_THE_LAYER']
p.write(size)

@@ -635,8 +884,23 @@ p.end()

it('should omit the defs mode if it is empty', function(done) {
var size = {type: 'size', box: [-1, -1, 1, 2], units: 'mm'}
p.on('data', function() {
expect(element).not.to.be.calledWith('defs')
done()
})
p.defs = []
p.layer = ['THIS_IS_THE_LAYER']
p.write(size)
p.end()
})
it('should finish any in-progress mask', function() {
p._mask = '<mask id="id_clear-1">'
p._maskId = 'id_clear-1'
p._maskBox = [1, 2, 3, 4]
p._mask = ['SOME STUFF']
p.end()
expect(p._mask).to.equal('')
expect(p.defs).to.equal('<mask id="id_clear-1"></mask>')
expect(element).to.be.calledWith('mask', {id: 'id_clear-1', fill: '#000', stroke: '#000'})
})

@@ -646,2 +910,3 @@

var offsets = [[0, 0], [0, 1], [1, 0], [1, 1]]
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 0.5, 0.5]})

@@ -651,11 +916,8 @@ p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})

expect(p._block).to.equal('')
expect(p.layer).to.equal([
'<use xlink:href="#id_block-1-1" x="0" y="0"/>',
'<use xlink:href="#id_block-1-1" x="0" y="1000"/>',
'<use xlink:href="#id_block-1-1" x="1000" y="0"/>',
'<use xlink:href="#id_block-1-1" x="1000" y="1000"/>'
].join(''))
expect(element).to.be.calledWith('use', {'xlink:href': '#id_block-1-1', x: 0, y: 0})
expect(element).to.be.calledWith('use', {'xlink:href': '#id_block-1-1', x: 0, y: 1000})
expect(element).to.be.calledWith('use', {'xlink:href': '#id_block-1-1', x: 1000, y: 0})
expect(element).to.be.calledWith('use', {'xlink:href': '#id_block-1-1', x: 1000, y: 1000})
})
})
})

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc