Code Monkey home page Code Monkey logo

psd's People

Contributors

alexanderspevak avatar alexspevak avatar anwynaoki avatar arecsu avatar dlehdanakf avatar pastelmind avatar scoiatael avatar simdaesoo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

psd's Issues

Investigate if we need to implement ZIP compression support

Adobe's PSD file format document claims that there are four compression methods for image channels: uncompressed (0), RLE (1), ZIP (2), ZIP with prediction (3). However, I have yet to find a PSD file that uses compression methods 2 and 3. If they do exist, we may have to support them as well.

What to do

  • Attempt to find or obtain a PSD file that uses compression methods 2 and 3
  • Find other PSD parsers that support methods 2 or 3
  • Decide if we want to implement ZIP compression support

Prior art

`FontCaps` not working?

I'm trying to know whether a text is capitalized.

I found the layer.layerFrame.layerProperties.textProperties.ResourceDict.StyleSheetSet.StyleSheetData.FontCaps property but it is always set to 0.
Whereas on Photoshop, the text is correctly set to capitalized:
image

Is there anyone who can help to point out where I can get the correct property to see if a text is capitalized or not? 😄

i have a question

sorry,when i use psd to get the font but i cannot get color of font

Project goals

This is a discussion topic and a work in progress.

We should specify our project goals and/or principles to help us focus on the right things.

What goals?

Some suggestions based on our work so far:

  • Correctness
  • Minimal set of wanted features
  • Zero-dependency
  • Performance: improve speed, reduce memory footprint

Where?

Ideally, the project goals should be part of the documention, perhaps in a CONTRIBUTING.md file. We may also put them in the project wiki (currently not enabled).

Other

For now, we can link to this issue when we mention our project goals and/or principles.

ICC profile

One of features available in psd-tools is ICC profile handling.

This is done by extracting relevant tag and then converting image data after extraction.

This in turns uses Python bindings for littlecms (https://pypi.org/project/littlecms/). In our case this won't fly - closest thing I was able to find are low-level binding based on emscripten.

So the questions are:

  • should this library handle the conversion or only expose information for ICC (and let users worry about converting themselves)? If former, is importing emscripten littlecms an ok solution? I'd rather create bindings ourselves than import npm library of dubious quality.
  • can I start working on it? :) I suppose the first step would be to look into alternative libraries for conversion.

Help importing project into node js project

I'm attempting to import the library into a node js project and I'm getting some errors. I'm working on getting up to speed on all things node js but I am able to use other libraries using requires. I'm also using Typescript.

At the top of module, myclass.ts I have:
import Psd from "@webtoon/psd";

This generates this error:

[Error [ERR_REQUIRE_ESM]: Must use import to load ES Module:
require() of ES modules is not supported.
require() of /Users/project/node_modules/@webtoon/psd/dist/index.js from /Users/project/MyProjectClass.ts is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/project/node_modules/@webtoon/psd/package.json.

It doesn't make sense to me what to do with that information.

node composite error

hi
I'm trying to use this library at server.

When I try to composite psd files, atob reference error is occured.

Also when I change atob to buffer, below error is occured.
TypeError: WebAssembly.instantiate(): Import #0 module="./webtoon_psd_decoder_bg.js" error: module is not an object or functiom

How can I use composite?

Thankyou

Investigate providing both WebAssembly- and vanilla JS-based decoders

When we replaced the asm.js image decoder with a WebAssembly-based one (#20), we gained performance and resilience† at the expense of increased bundle size and breaking compatibility. This was fine for our use case--but what if people want to use our library in a runtime that does not support WebAssembly (or asm.js for that matter)? What if bundle size is critical?

asm.js is dead tech and there are development tools (e.g. esbuild) that do not support it. See #20 (comment)

In our internal experiments, a properly optimized vanilla JS decoder was ~15% slower in Chrome (V8), and ~50% slower in Firefox and Safari. Since Node.js uses V8, we may expect similar performance differences in Node.js. These differences may be acceptable to those who want a tiny bundle--or don't have the luxury of WebAssembly.

Perhaps we could provide two decoders (vanilla JS and WebAssembly) and let the user choose?

Brainstorming Strategies

Create separate bundles for vanilla JS and WebAssembly, possibly exposing them as separate endpoints:

import Psd from '@webtoon/psd'         // WebAssembly
import Psd from '@webtoon/psd/vanilla' // Vanilla JS

Create two parse() methods and export them:

import { parse, parseWithJs } from '@webtoon/psd'

const psd1 = parse(arrayBuffer)
psd1.composite() // WebAssembly

const psd2 = parseWithJs(arrayBuffer)
psd2.composite() // Vanilla JS

Create two composite() methods:

import { parse, composite, compositeWithJs } from '@webtoon/psd'

const psd = parse(arrayBuffer)
composite(psd)       // WebAssembly
compositeWithJs(psd) // Vanilla JS

Expand test suite

It would be nice to expand our test suite and test our library more rigorously. I don't have a clear plan, so I'm open to suggestions.

Some ideas:

Opening file

  • Fail: Empty file
  • Fail: File is too large (>2GB)

File Header

  • Fail: File Header is too small/truncated
  • Fail: Normal file with invalid signature (not ‘8BPS’, e.g. ‘FAIL’)
  • Fail: Normal file with lowercase signature (‘8bps’)
  • Fail: Normal file with invalid version (not 1)
    • PSB uses version number 2
  • Fail: Reserved segment contains nonzero values
  • Fail: Number of channels exceeds max (>56)
  • Fail: Image height > 30000
    • PSB has image height limit of 300,000
  • Fail: Image width > 30000
    • PSB has image width limit of 300,000
  • Fail: Invalid bits per channel (not in { 1, 8, 16, 32 })
  • Fail: Invalid color mode (not in { 0, 1, 2, 3, 4, 7, 8, 9 })

Color Mode

  • Fail: Missing or truncated color mode section
  • Success: Non-(indexed (2) or duotone (8)) image has empty Color Mode section
  • Fail: Non-indexed/duotone image has non-empty Color Mode section
  • Fail: Indexed image has invalid color table size (not 768)
  • ???: Duotone images have undocumented format—do we want to handle this?
    • Other apps should ignore/preserve this section if the image is duotone

Resources

  • Fail: Missing or truncated resources section

Resource block

  • Fail: Resource block is too small/truncated
  • Fail: Resource block has invalid signature (not ‘8BIM’)
  • Fail: Resource block has lowercase signature (‘8bim’)
  • Fail: Resource block has duplicate ID
  • Fail: Resource block has unknown ID value
(see https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_38034 for list of valid IDs)
  • Fail: Resource block name string is invalid
  • Fail: Resource block name string is not padded with zero (should we even check this?)
  • Fail: Resource data section is invalid (need more info)

Layer and Mask

  • Fail: L&M is missing or truncated

Layer Info

  • Fail: Layer length is not a multiple of 2
  • Fail: Layer info section is missing or truncated
  • Fail: Layer records are bad
  • Fail: Channel image data is bad

Layer Record

  • Fail: Blend mode signature is invalid (not ‘8BIM’)
  • Fail: Blend mode key is unknown (see Layer records for the full list)
  • Fail: Clipping value is invalid
  • Fail: Bits 5~7 are nonzero (should we even check this?)
  • Fail: Filler is nonzero
Layer mask/adjustment layer data
  • Fail: Length is not one of 0, 20, 36
  • Fail: Default color is neither 0 or 255
  • Fail: Bits 5~7 are nonzero (should we even check this?)
Layer blending ranges
  • Fail: Section is missing or truncated
Channel Image Data
  • Fail: Invalid compression method (not one of 0, 1, 2, 3)
  • Fail: Image data is missing or truncated
Additional Layer Info
  • Fail: ALI section is missing or truncated
  • Fail: Unknown key (see

Global layer mask info

  • Fail: Section is missing or truncated
  • Fail: Length is too short (< 13)

Image Data

  • Fail: Invalid compression method
  • Fail: Missing or truncated compressed data
  • Fail: Compressed data does not match compression method

SourceMaps not working against minified content

Not sure why the code is not getting sourcemapped to TypeScript (unminified), I see the //#sourceMappingURL=index.js.map in the dist/index.js file, is this fixable? See my error from #100:

my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:397
    throw new Le();
          ^
          
Le
    at wt (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:397:11)
    at fn (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:1188:76)
    at Rn (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:1490:13)
    at Function.parse (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:1982:15)
    at <anonymous> (my-app/tmp/psd.ts:162482:21)
    at Object.<anonymous> (my-app/tmp/psd.ts:162490:4)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Object.F (my-app/node_modules/.pnpm/@[email protected]/node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Module._load (node:internal/modules/cjs/loader:1023:12)

Node.js v20.10.0

I am using Next.js, do I need to do anything fancy do you think, to get your sourcemaps?

Parse EngineData

We got an internal request regarding the "EngineData" field of the Type Tool Object Setting structure, which encodes information about a text layer.

The Type Tool Object Setting structure is part of the Additional Layer Information record, inside the Layer and Mask Information Section of a PSD file.

PSD
  Layer and Mask Information
    Layer Info
      Layer Records
        Additional Layer Information
          Type Tool Object Setting
            text data (Descriptor classId == "TxLr")
              EngineData

The EngineData field is encoded in an undocumented format (nicknamed "EngineData format") invented by Adobe. While @webtoon/psd does not currently parse this segment, we may have to do so in the future, in order to support additional features related to text layers.

Prior work

Several PSD parsers can handle the EngineData format:

However, we MAY want to write a custom parser in TypeScript, in accordance with the Zero-Dependency design goal (see #5).

Retrieving mask data

Hi there, great project and the 🚀 is impressive!

I have one question related to masked layers. Basically, I would like to retrieve an image buffer for the mask data (similar to retrieving the composite of the Psd file or the Layer, as an Uint8ClampedArray).

However, it's not clear how to consume this data. I understand that I can retrieve the masks bounding box reading the layer.maskData Objects left, right, top, bottom values. I also see that there is an ArrayBuffer inside the layer.layerFrame.userMask.data Object.

However, the length of said buffer does not match with the size of the mask (as it expected to be width * height * 4 in length). I was thinking that this buffer contains the alpha channel or gray scale data only, however after converting this to fit in an RGBA sized buffer, the rendered data does not reflect the original masks shape, with pixels basically all over the place. I also noticed that the Layer class has an async method getUserMask|getRealUserMask which seems to return RGBA-sized values, but once more the rendered data does not reflect the masks shape.

As such, I'm not sure how to read and render the mask buffer object. Is there something obvious I'm overlooking with regards to the mask size ?

Update Text in Layer

its possible update a text in layer?
Example..
I have a layer with text "Hello World", I like the change to another text.

Its possible the make this using this lib?

Thanks.

Keep the document width and height , when exporting a layer (animations)

H!

Maybe someone can use this in the future.
I have made some animations inside photoshop, within the same document.
each layer is a frame, and have different sizes.

When you export the layer, then you get different sizes.
With this code, you will keep the document sizes, and the correct layer position in the document.
So you can replay the animation again, inside a game or whatever.

import * as fs from "fs"
import Psd from "@webtoon/psd"
import { createCanvas, createImageData } from 'canvas'

const psdData = fs.readFileSync("talk-sideways.psd")
const psdFile = Psd.parse(psdData.buffer)

    let layerPixelData = await layer.composite()

    const canvas = createCanvas(psdFile.width, psdFile.height)
    const ctx = canvas.getContext('2d')

    const image = createImageData(layerPixelData, layer.width, layer.height)
    ctx.putImageData(image, layer.left, layer.top)

    const out = fs.createWriteStream('out.png')
    const stream = canvas.createPNGStream()
    stream.pipe(out)
    out.on('finish', () => console.log('The PNG file was created.'))

Proper instructions for contributions is needed

Firstly guide on how to install all the dependencies and run the project either in contribution.md or any other readme would help new contributors to onboard.

Secondly, guideline on what or how to contribute would be useful.

visible always true ?

Hi 👋 I'm trying to get information from .psb about whether the layer is disabled or not, for some reason I always get true, is this normal?
2022-04-26_03-38
2022-04-26_03-39
or am I looking somewhere wrong ?

Can I programatically add image to the layer?

So instead of me going to photopea, uploading psd file, selecting layer, adding an image, resizing to fit the layer, exporting to png...
I would like to do this programatically in javascript (server side or client side). I want to write a function that accepts psd file, image file and options. It should output a .png file. Am I in the right place? Can I do this with "webtoon"? I've found libraries like "psd.js" but still not sure how can I achieve that. Can anyone give me an example? Your help is very much appreciated!

how to recover color with hsb?

I have a problem when the color have hsb attribute like that.

image

image

top is export by photoshop, bottom is by @webtoon/psd. color is grayer than designed.

is there any good way to solve it, thanks.

How to save a layer to a image file

In the documentation it says you can export a layer to pixel data. How would you save that to a PNG? I'm using node js.

// Extract the pixel data of a layer, with all layer and layer group effects applied
var layerPixelData = await layer.composite();

upload psd parse error

image
image

image

I uploaded a psd file and reported an error when parsing. The error screenshot is shown above. After I open the source code to modify the code and recompile, it can be used normally. The location of the modified source code is as follows
image

Here is my psd file

test_psd.zip

Benchmark fails occasionally due to imageDecodeTime being negative

Benchmark for ag-psd occasionally fails with the following error: imageDecodeTime must be a nonnegative number

This is probably happening because of the rather fragile (and broken) way we currently use to measure the image decode time, i.e. by subtracting the parse time (measured separately) from the total render time:

imageDecodeTime:
parseAndImageRenderEnd - parseAndImageRenderBegin - parseTime,

The benchmark app is long overdue a rewrite anyway; I'm thinking of writing it in React.

Opening a PSD file throws "invalid typed array length" error

I'm trying to open a PSD file i haved created from Photopea into the demo and after selecting the file, the preview did not worked and instead the demo threw an error:

image

When dissecting the stack trace, it leads me to this code which is in line 253 254:
image

Hope it helps 😉 hoping to use it for a project

layer style no parser

let nodeFX = node.get('objectEffects').data; 
// data is not defined is the node has no fx options.

This property contains all the informations from the FX (also called blending options) from Photoshop. (e.g. Drop Shadow, Inner Shadow ...).

{
    data: {
		ebbl: [Object] // Bevel and Emboss
		FrFX: [Object] // Stroke 
		IrSh: [Object] // Inner Shadow
		IrGl: [Object] // Inner Glow
		ChFX: [Object] // Satin
		SoFi: [Object] // Color Overlay
		GrFl: [Object] // Gradient Overlay
		patternFill: [Object] // Pattern Overlay
		OrGl: [Object] // Outer Glov
		DrSh: [Object] // Drop Shadow
    }
}

Investigate passing to/from web workers

A task that parses a large PSD file can often exceed the 16ms budget in web browsers and block UI updates. To avoid this, it's natural to attempt parsing a PSD file in a Web Worker via postMessage() and sending the results back. It would be nice to support this use case.

Unfortunately, this is currently impossible because the Psd object cannot be reliably (de)serialized with the structured clone algorithm. Psd provides instance methods and getters, which are lost when passed to/from a Web Worker.

We have some ideas, but would still like feedback:

  1. Convert the Psd class (and other classes) into plain, structured clone-able objects
    • Instance methods will have to be converted to plain functions
    • This will be quite disruptive as it is backwards incompatible
  2. Create Psd.prototype.serialize() that generates a structured clone-able implementation, as well as Psd.deserialize() that reconstructs the original Psd object
    • This will be clunky and leak implementation details all over

text layer's textProperties no parsed corretly

I try to get font style.but it parse result always same(in fact not).

The StyleSheetData like FontSize, FillColor,StrokeColor and other property are same,it seems like no parse yet?

image

PNG converted to PSD with webtoon/psd not parseable with `Psd.parse(buffer)` (PSD generated from PNG with imagemagick-wasm)?

I created this PSD file.psd.zip from this PNG file.png.zip, using https://github.com/dlemstra/magick-wasm . That magick library (for the browser) works on converting PNG to JPG/etc., and I saved the bytes output from the selected browser file to a .psd file on my computer, and that is the contents of file.psd.zip. That psd seems to render on my Mac (and with CMD+space it shows large):

Screenshot 2024-01-23 at 12 17 38 PM

That png itself was generated from another png (I'm playing with magick-wasm), if that matters. But it renders as a PSD locally if I say the bytes to a .psd file, but I get this error in Psd.parse(arrayBuffer):

my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:397
    throw new Le();
          ^
          
Le
    at wt (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:397:11)
    at fn (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:1188:76)
    at Rn (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:1490:13)
    at Function.parse (my-app/node_modules/.pnpm/@[email protected]/node_modules/@webtoon/psd/dist/index.js:1982:15)
    at <anonymous> (my-app/tmp/psd.ts:162482:21)
    at Object.<anonymous> (my-app/tmp/psd.ts:162490:4)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Object.F (my-app/node_modules/.pnpm/@[email protected]/node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Module._load (node:internal/modules/cjs/loader:1023:12)

Node.js v20.10.0

This is occurring whether or not I'm calling Psd.parse in Node.js or the browser. So it fails to parse the PSD, and so I can't render the PSD in the browser.

Any ideas what is wrong with this PNG, or why this isn't parsing?

I get the same error when trying to parse a JPEG as well, so not sure.

My messy code, with the PSD functionality temp hacked in for now:

useEffect(() => {
  if (!files?.length) {
    return
  }

  const file = files[0]
  setInput(file)
  // https://www.npmjs.com/package/wasm-imagemagick
  file.arrayBuffer().then(async data => {
    const ir = new FileReader()
    ir.onloadend = () => {
      setInputString(ir.result as string)
    }
    ir.readAsDataURL(file)
    if (data.byteLength <= bytes('16mb')) {
      if (outputFormat === 'psd') {
        const canvasElement = canvasRef.current
        if (canvasElement) {
          try {
            console.log(bufferToString(await file.arrayBuffer()))

            function bufferToString(buf: ArrayBuffer) {
              var view = new Uint8Array(buf)
              var array: Array<string> = []
              Array.prototype.forEach.call(view, item => {
                array.push(
                  '0x' +
                    (item as number).toString(16).padStart(2, '0'),
                )
              })
              return `[ ${array.join(',\n  ')} ]`
            }
            const psdFile = Psd.parse(await file.arrayBuffer())
            const context = canvasElement.getContext('2d')
            const compositeBuffer = await psdFile.composite()
            const imageData = new ImageData(
              compositeBuffer as Uint8ClampedArray,
              psdFile.width as number,
              psdFile.height as number,
            )
            setShowCanvas(true)

            canvasElement.width = psdFile.width
            canvasElement.height = psdFile.height

            context?.putImageData(imageData, 0, 0)
          } catch (e) {
            console.log(e)
          }
        }
      } else {
        readWithImageMagickCallback(
          new Uint8Array(data),
          (err, image) => {
            if (err) {
              console.log(err)
              return setInputError(err.message as string)
            } else if (image) {
              writeWithImageMagick(image, outputFormat)
                .then(out => {
                  setInputError(undefined)
                  const path = file.name.split('.')
                  if (path.length > 1) {
                    path.pop()
                  }
                  const blob = new Blob([out])
                  setOutput(blob)
                  setOutputFileName(
                    `${path.join('.')}.${outputFormat.toLowerCase()}`,
                  )
                  // downloadBlob(
                  //   blob,
                  //   `${path.join('.')}.${outputFormat.toLowerCase()}`,
                  // )

                  const or = new FileReader()
                  or.onloadend = () => {
                    setOutputString(or.result as string)
                  }
                  or.readAsDataURL(blob)
                })
                .catch(error => {
                  setInputError(error.message as string)
                })
            }
          },
        )
      }
    } else {
      setInputError(
        `File should be less than 16mb so it doesn't crash the browser.`,
      )
    }
  })
}, [files, outputFormat, canvasRef])

Layer hide and unhide

Hello,

Is it possible to unhide a hidden layer. I have a psd file and I would like to hide and unhide options based on my confirmation file.

Thanks,

How can I check the LayerId?

I'm using the module very well.
Thank you for working on it.

How can I check the layerID?

I think there are GroupID and Regular-ID(?), but I don't have an LayerID to specify the layer.

Is there a way to check it out?

Please check.
Thank you.

Replace Smart Object with image in-browser

Hi there,
Can someone help me build this feature for a client,
I want a way to get all smart objects inside the psd, and then generate file inputs for each smart object, and when the user uploads an image, i want to display live changes

Investigate tools for build orchestration in monorepo

Currently, packages in our monorepo have the following dependency graph:

psd-decoder psd <-- benchmark, example-browser, example-node, benchmark

When building a package, we must ensure that its dependencies are built first, and in order. Furthermore, when building a package in watch mode, we must launch watchers for the dependencies as well. Managing this with NPM scripts is tricky and error-prone, and I already had to push fixes such as #26 and #27.

List of tools

* npm-run-all has been abandoned for some time. npm-run-all2 seems to be the most well-maintained fork.
† Since version 5, Lerna bundles and is powered by Nx, so there's little point in installing it for new projects--we should use Nx directly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.