fabricjs / fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser
http://fabricjs.com
Other
29.03k stars 3.51k forks source link

Make Image implementation available before load: How to create an instance of Fabric image from Buffer object #6047

Open Shohin93 opened 4 years ago

Shohin93 commented 4 years ago

Is there a way to build a Fabric instance from Buffer object, for example, from the following buffer: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 48 89 95 97 ... >. I'm passing a buffer (fetched from S3 bucket) from another service/application to my current application where I do image manipulations. There's this API fromObject(object, callback) (http://fabricjs.com/docs/fabric.Image.html), but passing the above buffer does not seem to be doing job. Anybody knows how to do it?

asturur commented 4 years ago

If you are retriving the buffer from a service, is probably coming in a format that is not Buffer. What format is it? I would imagine is a url or a data url.

Shohin93 commented 4 years ago

@asturur it's something like this:

{ AcceptRanges: 'bytes',
  LastModified: 2019-12-26T16:10:00.000Z,
  ContentLength: 220518,
  ETag: '"c6288cc91a3993c207a96d169a4921ab"',
  ContentType: 'application/octet-stream',
  Metadata:
   { extension: 'png',
     mime: 'image/png',
     width: '1019',
     height: '776',
     name: 'Screen_Shot_2019_12_18_at_11_01_04_AM' },
  Body: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 03 fb 00 00 03 08 08 06 00 00 00 97 7e f5 8d 00 00 0a b6 69 43 43 50 49 43 43 20 50 72 6f 66 69 ... > }

So, body is Buffer. Is there any api in in Fabric that works with buffers? fromObject doesn't really do the job

asturur commented 4 years ago

oh ok you are getting the file itself. if the api returning you the file is GET and not authenticated, you can pass that URL to the fromURL image method and you should be fine.

Otherwise, assuming you are running under node, the only thing you could try to do is to instantiate a new HTMLImageElement using fabric.util.createImage, then you could then try to access the node-canvas implementation of that image using fabric.util.getNodeCanvas and then check the node-canvas docs on how to load an image from Buffer.

(i think is image.src = Buffer but i may be wrong )

Shohin93 commented 4 years ago

@asturur is it something like this:

const createdImage = fabric.util.createImage();
const nodeCanvas = fabric.util.getNodeCanvas(createdImage);
nodeCanvas.src = url;

? nodeCanvas returns undefined

Also, tried:

let image = fabric.util.createImage();
image.onload = function() {
      let nodeCanvasImage = fabric.util.getNodeCanvas(image);
      nodeCanvasImage.src = url;
}

and it doesn't seem to go inside the function()

I'm using "fabric": "2.7.0"

asturur commented 4 years ago

Are you sure node-canvas is properly installed in your app?

Normally having an undefined from that function means that the node canvas didn t install properly.

fabric 2 uses node-canvas 1.6.x, i would suggest you to give a test to fabric 3, changes are really small and node experience should be improved, also node canvas 2 is so much better.

Create a basic index.js that reproduces the failure if you can t solve ( that 3 or 4 lines of code, but with the requires and everything that is necessary for them to fail ) and post it here

Shohin93 commented 4 years ago

@asturur upgraded packages to the latest (tried also upgrading fabric to 3.0.0), but no luck.

First issue is it still gives undefined on fabric.util.getNodeCanvas(image). Second one is fabric.util.createImage() for fabric 3.x.x returns empty object HTMLImageElement {} and for fabric 2.x.x returns the following:

HTMLImageElement {
[Symbol(impl)]:
   HTMLImageElementImpl {
     _eventListeners: {},
     _core:
      { DOMException: [Object],
        NamedNodeMap: [Function: NamedNodeMap],
        Attr: [Function: Attr],
        Node: [Object],
        Element: [Function: Element],
        DocumentFragment: [Function: DocumentFragment],
        HTMLDocument: [Function: Document],
        Document: [Function: Document],
        XMLDocument: [Function: XMLDocument],
        CharacterData: [Function: CharacterData],
        Text: [Function: Text],
        CDATASection: [Function: CDATASection],
        ProcessingInstruction: [Function: ProcessingInstruction],
        Comment: [Function: Comment],
        DocumentType: [Function: DocumentType],
        DOMImplementation: [Function: DOMImplementation],
        Event: [Object],
        CustomEvent: [Function: CustomEvent],
        MessageEvent: [Function: MessageEvent],
        ErrorEvent: [Function: ErrorEvent],
        HashChangeEvent: [Function: HashChangeEvent],
        FocusEvent: [Function: FocusEvent],
        PopStateEvent: [Function: PopStateEvent],
        UIEvent: [Function: UIEvent],
        MouseEvent: [Function: MouseEvent],
        KeyboardEvent: [Object],
        TouchEvent: [Function: TouchEvent],
        ProgressEvent: [Function: ProgressEvent],
        EventTarget: [Function: EventTarget],
        Location: [Function: Location],
        History: [Function: History],
        Blob: [Function: Blob],
        File: [Function: File],
        FileList: [Function: FileList],
        DOMParser: [Function: DOMParser],
        FormData: [Function: FormData],
        HTMLElement: [Function: HTMLElement],
        HTMLAnchorElement: [Function: HTMLAnchorElement],
        HTMLAppletElement: [Function: HTMLAppletElement],
        HTMLAreaElement: [Function: HTMLAreaElement],
        HTMLAudioElement: [Function: HTMLAudioElement],
        HTMLBaseElement: [Function: HTMLBaseElement],
        HTMLBodyElement: [Function: HTMLBodyElement],
        HTMLBRElement: [Function: HTMLBRElement],
        HTMLButtonElement: [Function: HTMLButtonElement],
        HTMLCanvasElement: [Function: HTMLCanvasElement],
        HTMLDataElement: [Function: HTMLDataElement],
        HTMLDataListElement: [Function: HTMLDataListElement],
        HTMLDialogElement: [Function: HTMLDialogElement],
        HTMLDirectoryElement: [Function: HTMLDirectoryElement],
        HTMLDivElement: [Function: HTMLDivElement],
        HTMLDListElement: [Function: HTMLDListElement],
        HTMLEmbedElement: [Function: HTMLEmbedElement],
        HTMLFieldSetElement: [Function: HTMLFieldSetElement],
        HTMLFontElement: [Function: HTMLFontElement],
        HTMLFormElement: [Function: HTMLFormElement],
        HTMLFrameElement: [Function: HTMLFrameElement],
        HTMLFrameSetElement: [Function: HTMLFrameSetElement],
        HTMLHeadingElement: [Function: HTMLHeadingElement],
        HTMLHeadElement: [Function: HTMLHeadElement],
        HTMLHRElement: [Function: HTMLHRElement],
        HTMLHtmlElement: [Function: HTMLHtmlElement],
        HTMLIFrameElement: [Function: HTMLIFrameElement],
        HTMLImageElement: [Function: HTMLImageElement],
        HTMLInputElement: [Function: HTMLInputElement],
        HTMLLabelElement: [Function: HTMLLabelElement],
        HTMLLegendElement: [Function: HTMLLegendElement],
        HTMLLIElement: [Function: HTMLLIElement],
        HTMLLinkElement: [Function: HTMLLinkElement],
        HTMLMapElement: [Function: HTMLMapElement],
        HTMLMediaElement: [Object],
        HTMLMenuElement: [Function: HTMLMenuElement],
        HTMLMetaElement: [Function: HTMLMetaElement],
        HTMLMeterElement: [Function: HTMLMeterElement],
        HTMLModElement: [Function: HTMLModElement],
        HTMLObjectElement: [Function: HTMLObjectElement],
        HTMLOListElement: [Function: HTMLOListElement],
        HTMLOptGroupElement: [Function: HTMLOptGroupElement],
        HTMLOptionElement: [Function: HTMLOptionElement],
        HTMLOutputElement: [Function: HTMLOutputElement],
        HTMLParagraphElement: [Function: HTMLParagraphElement],
        HTMLParamElement: [Function: HTMLParamElement],
        HTMLPreElement: [Function: HTMLPreElement],
        HTMLProgressElement: [Function: HTMLProgressElement],
        HTMLQuoteElement: [Function: HTMLQuoteElement],
        HTMLScriptElement: [Function: HTMLScriptElement],
        HTMLSelectElement: [Function: HTMLSelectElement],
        HTMLSourceElement: [Function: HTMLSourceElement],
        HTMLSpanElement: [Function: HTMLSpanElement],
        HTMLStyleElement: [Function: HTMLStyleElement],
        HTMLTableCaptionElement: [Function: HTMLTableCaptionElement],
        HTMLTableCellElement: [Function: HTMLTableCellElement],
        HTMLTableColElement: [Function: HTMLTableColElement],
        HTMLTableDataCellElement: [Function: HTMLTableDataCellElement],
        HTMLTableElement: [Function: HTMLTableElement],
        HTMLTableHeaderCellElement: [Function: HTMLTableHeaderCellElement],
        HTMLTimeElement: [Function: HTMLTimeElement],
        HTMLTitleElement: [Function: HTMLTitleElement],
        HTMLTableRowElement: [Function: HTMLTableRowElement],
        HTMLTableSectionElement: [Function: HTMLTableSectionElement],
        HTMLTemplateElement: [Function: HTMLTemplateElement],
        HTMLTextAreaElement: [Function: HTMLTextAreaElement],
        HTMLTrackElement: [Object],
        HTMLUListElement: [Function: HTMLUListElement],
        HTMLUnknownElement: [Function: HTMLUnknownElement],
        HTMLVideoElement: [Function: HTMLVideoElement],
        StyleSheet: [Function: StyleSheet],
        MediaList: [Function: MediaList],
        CSSStyleSheet: [Function: CSSStyleSheet],
        CSSRule: [Object],
        CSSStyleRule: [Object],
        CSSMediaRule: [Function: CSSMediaRule],
        CSSImportRule: [Function: CSSImportRule],
        CSSStyleDeclaration: [Function: CSSStyleDeclaration],
        StyleSheetList: [Function: StyleSheetList],
        XPathException: [Object],
        XPathExpression: [Function: XPathExpression],
        XPathResult: [Object],
        XPathEvaluator: [Function: XPathEvaluator],
        HTMLCollection: [Function: HTMLCollection],
        NodeFilter: [Object],
        NodeIterator: [Function: NodeIterator],
        NodeList: [Function: NodeList],
        XMLHttpRequestEventTarget: [Function: XMLHttpRequestEventTarget],
        XMLHttpRequestUpload: [Function: XMLHttpRequestUpload],
        DOMTokenList: [Function: DOMTokenList],
        URL: [Function: URL],
        Window: [Function: Window] },
     _ownerDocument:
      DocumentImpl {
        _eventListeners: {},
        _core: [Object],
        _ownerDocument: [Circular],
        _childNodesList: [Object],
        _childrenList: null,
        _version: 4,
        _memoizedQueries: {},
        nodeType: 9,
        _parsingMode: 'html',
        _htmlToDom: [Object],
        _implementation: [Object],
        _defaultView: [Object],
        _global: [Object],
        _documentElement: [Object],
        _ids: {},
        _attached: true,
        _currentScript: null,
        _cookieJar: [Object],
        _contentType: undefined,
        _encoding: 'UTF-8',
        _URL: [Object],
        _origin: 'null',
        _location: [Object],
        _history: [Object],
        _activeNodeIterators: [],
        _activeNodeIteratorsMax: 10,
        _referrer: '',
        _lastModified: '12/29/2019 21:57:47',
        _queue: [Object],
        _customResourceLoader: undefined,
        _pool: [Object],
        _agentOptions: [Object],
        _strictSSL: true,
        _proxy: undefined,
        _requestManager: [Object],
        readyState: 'complete',
        _lastFocusedElement: null,
        [Symbol(DOM SymbolTree)]: [Object],
        [Symbol(wrapper)]: [Object] },
     _childNodesList: null,
     _childrenList: null,
     _version: 0,
     _memoizedQueries: {},
     nodeType: 1,
     scrollTop: 0,
     scrollLeft: 0,
     _namespaceURI: 'http://www.w3.org/1999/xhtml',
     _prefix: null,
     _localName: 'img',
     _attributes: NamedNodeMap { [Symbol(NamedNodeMap internal)]: [Object] },
     _tabIndex: 0,
     _settingCssText: false,
     _clickInProgress: false,
     _style:
      CSSStyleDeclaration {
        _values: {},
        _importants: {},
        _length: 0,
        _onChange: [Function] },
     [Symbol(DOM SymbolTree)]:
      SymbolTreeNode {
        parent: null,
        previousSibling: null,
        nextSibling: null,
        firstChild: null,
        lastChild: null,
        childrenVersion: 0,
        childIndexCachedUpTo: null,
        cachedIndex: -1,
        cachedIndexVersion: NaN },
     [Symbol(wrapper)]: [Circular] } }

Not sure what causes this ^

Upgraded packages: When my app was on fabric: 1.x (before upgrading it to 2.x), passing buffer to fabric.Image.fromURL would work just fine 😄.

"dependencies": {
    "fabric": "3.6.0"
  },
  "peerDependencies": {
    "canvas": "^2.6.1",
    "jsdom": "^15.2.1"
  }

Sample code:

const createdImage = fabric.util.createImage();
console.log("\n createdImage", createdImage, "\n");
const nodeCanvas = fabric.util.getNodeCanvas(createdImage);
console.log("\n nodeCanvas", nodeCanvas, "\n");

Output:

createdImage HTMLImageElement {
  [Symbol(impl)]:
   HTMLImageElementImpl {
     _eventListeners: {},
     _core:
      { DOMException: [Object],
        NamedNodeMap: [Function: NamedNodeMap],
       .......
       .......
       ....... // Shows an empty object HTMLImageElement {} for fabric 3.x

nodeCanvas undefined // For both fabric 2.x and 3.x it returns undefined
Shohin93 commented 4 years ago

@asturur actually, i don't see any _canvas or _image property returned from fabric.jsdomImplForWrapper(element) (which is called from getNodeCanvas()):

HTMLImageElementImpl {
  _eventListeners: [Object: null prototype] {},
  _ownerDocument:
   DocumentImpl {
     _eventListeners: [Object: null prototype] { load: [Array] },
     _ownerDocument: [Circular],
     _childNodesList:
      NodeListImpl {
        _list: [Array],
        _isLive: true,
        _version: 6,
        _element: [Circular],
        _query: [Function: query],
        [Symbol(wrapper)]: [NodeList] },
     _childrenList: null,
     _version: 6,
     _memoizedQueries: {},
     _registeredObserverList: [],
     _registeredHandlers: Set {},
     _eventHandlers: [Object: null prototype] {},
     nodeType: 9,
     _parsingMode: 'html',
     _implementation:
      DOMImplementationImpl {
        _ownerDocument: [Circular],
        [Symbol(wrapper)]: DOMImplementation {} },
     _defaultView:
      Window {
        onafterprint: [Getter/Setter],
        onbeforeprint: [Getter/Setter],
        onbeforeunload: [Getter/Setter],
        onhashchange: [Getter/Setter],
        onlanguagechange: [Getter/Setter],
        onmessage: [Getter/Setter],
        onmessageerror: [Getter/Setter],
        onoffline: [Getter/Setter],
        ononline: [Getter/Setter],
        onpagehide: [Getter/Setter],
        onpageshow: [Getter/Setter],
        onpopstate: [Getter/Setter],
        onrejectionhandled: [Getter/Setter],
        onstorage: [Getter/Setter],
        onunhandledrejection: [Getter/Setter],
        onunload: [Getter/Setter],
        onblur: [Getter/Setter],
        onerror: [Getter/Setter],
        onfocus: [Getter/Setter],
        onload: [Getter/Setter],
        onresize: [Getter/Setter],
        onscroll: [Getter/Setter],
        onabort: [Getter/Setter],
        onautocomplete: [Getter/Setter],
        onautocompleteerror: [Getter/Setter],
        oncancel: [Getter/Setter],
        oncanplay: [Getter/Setter],
        oncanplaythrough: [Getter/Setter],
        onchange: [Getter/Setter],
        onclick: [Getter/Setter],
        onclose: [Getter/Setter],
        oncontextmenu: [Getter/Setter],
        oncuechange: [Getter/Setter],
        ondblclick: [Getter/Setter],
        ondrag: [Getter/Setter],
        ondragend: [Getter/Setter],
        ondragenter: [Getter/Setter],
        ondragexit: [Getter/Setter],
        ondragleave: [Getter/Setter],
        ondragover: [Getter/Setter],
        ondragstart: [Getter/Setter],
        ondrop: [Getter/Setter],
        ondurationchange: [Getter/Setter],
        onemptied: [Getter/Setter],
        onended: [Getter/Setter],
        oninput: [Getter/Setter],
        oninvalid: [Getter/Setter],
        onkeydown: [Getter/Setter],
        onkeypress: [Getter/Setter],
        onkeyup: [Getter/Setter],
        onloadeddata: [Getter/Setter],
        onloadedmetadata: [Getter/Setter],
        onloadstart: [Getter/Setter],
        onmousedown: [Getter/Setter],
        onmouseenter: [Getter/Setter],
        onmouseleave: [Getter/Setter],
        onmousemove: [Getter/Setter],
        onmouseout: [Getter/Setter],
        onmouseover: [Getter/Setter],
        onmouseup: [Getter/Setter],
        onwheel: [Getter/Setter],
        onpause: [Getter/Setter],
        onplay: [Getter/Setter],
        onplaying: [Getter/Setter],
        onprogress: [Getter/Setter],
        onratechange: [Getter/Setter],
        onreset: [Getter/Setter],
        onsecuritypolicyviolation: [Getter/Setter],
        onseeked: [Getter/Setter],
        onseeking: [Getter/Setter],
        onselect: [Getter/Setter],
        onsort: [Getter/Setter],
        onstalled: [Getter/Setter],
        onsubmit: [Getter/Setter],
        onsuspend: [Getter/Setter],
        ontimeupdate: [Getter/Setter],
        ontoggle: [Getter/Setter],
        onvolumechange: [Getter/Setter],
        onwaiting: [Getter/Setter],
        _registeredHandlers: Set {},
        _eventHandlers: [Object: null prototype] {},
        _resourceLoader: [ResourceLoader],
        _globalProxy: [Circular],
        _document: [Document],
        _sessionHistory: [SessionHistory],
        _virtualConsole: [VirtualConsole],
        _runScripts: undefined,
        _top: [Circular],
        _parent: [Circular],
        _frameElement: null,
        _length: 0,
        _pretendToBeVisual: false,
        _storageQuota: 5000000,
        _commonForOrigin: [Object],
        _currentOriginData: [Object],
        _localStorage: Storage {},
        _sessionStorage: Storage {},
        length: [Getter],
        window: [Getter],
        frameElement: [Getter],
        frames: [Getter],
        self: [Getter],
        parent: [Getter],
        top: [Getter],
        document: [Getter],
        external: [Getter],
        location: [Getter],
        history: [Getter],
        navigator: [Getter],
        locationbar: [Getter],
        menubar: [Getter],
        personalbar: [Getter],
        scrollbars: [Getter],
        statusbar: [Getter],
        toolbar: [Getter],
        performance: [Getter],
        screen: [Getter],
        localStorage: [Getter],
        sessionStorage: [Getter],
        addEventListener: [Function: bound addEventListener],
        removeEventListener: [Function: bound removeEventListener],
        dispatchEvent: [Function: bound dispatchEvent],
        setTimeout: [Function],
        setInterval: [Function],
        clearInterval: [Function: bound stopTimer],
        clearTimeout: [Function: bound stopTimer],
        __stopAllTimers: [Function],
        postMessage: [Function],
        atob: [Function],
        btoa: [Function],
        FileReader: [Function],
        WebSocket: [Function],
        AbortSignal: [Function: AbortSignal],
        AbortController: [Function: AbortController],
        XMLHttpRequest: [Function],
        ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: [Function: Int8Array],
        Uint8Array: [Function: Uint8Array],
        Uint8ClampedArray: [Function: Uint8ClampedArray],
        Int16Array: [Function: Int16Array],
        Uint16Array: [Function: Uint16Array],
        Int32Array: [Function: Int32Array],
        Uint32Array: [Function: Uint32Array],
        Float32Array: [Function: Float32Array],
        Float64Array: [Function: Float64Array],
        stop: [Function],
        close: [Function],
        getComputedStyle: [Function],
        captureEvents: [Function],
        releaseEvents: [Function],
        console: [Object],
        name: '',
        status: '',
        devicePixelRatio: 1,
        innerWidth: 1024,
        innerHeight: 768,
        outerWidth: 1024,
        outerHeight: 768,
        pageXOffset: 0,
        pageYOffset: 0,
        screenX: 0,
        screenLeft: 0,
        screenY: 0,
        screenTop: 0,
        scrollX: 0,
        scrollY: 0,
        alert: [Function],
        blur: [Function],
        confirm: [Function],
        focus: [Function],
        moveBy: [Function],
        moveTo: [Function],
        open: [Function],
        print: [Function],
        prompt: [Function],
        resizeBy: [Function],
        resizeTo: [Function],
        scroll: [Function],
        scrollBy: [Function],
        scrollTo: [Function],
        [Symbol(named property tracker)]: [NamedPropertiesTracker] },
     _global:
      Window {
        onafterprint: [Getter/Setter],
        onbeforeprint: [Getter/Setter],
        onbeforeunload: [Getter/Setter],
        onhashchange: [Getter/Setter],
        onlanguagechange: [Getter/Setter],
        onmessage: [Getter/Setter],
        onmessageerror: [Getter/Setter],
        onoffline: [Getter/Setter],
        ononline: [Getter/Setter],
        onpagehide: [Getter/Setter],
        onpageshow: [Getter/Setter],
        onpopstate: [Getter/Setter],
        onrejectionhandled: [Getter/Setter],
        onstorage: [Getter/Setter],
        onunhandledrejection: [Getter/Setter],
        onunload: [Getter/Setter],
        onblur: [Getter/Setter],
        onerror: [Getter/Setter],
        onfocus: [Getter/Setter],
        onload: [Getter/Setter],
        onresize: [Getter/Setter],
        onscroll: [Getter/Setter],
        onabort: [Getter/Setter],
        onautocomplete: [Getter/Setter],
        onautocompleteerror: [Getter/Setter],
        oncancel: [Getter/Setter],
        oncanplay: [Getter/Setter],
        oncanplaythrough: [Getter/Setter],
        onchange: [Getter/Setter],
        onclick: [Getter/Setter],
        onclose: [Getter/Setter],
        oncontextmenu: [Getter/Setter],
        oncuechange: [Getter/Setter],
        ondblclick: [Getter/Setter],
        ondrag: [Getter/Setter],
        ondragend: [Getter/Setter],
        ondragenter: [Getter/Setter],
        ondragexit: [Getter/Setter],
        ondragleave: [Getter/Setter],
        ondragover: [Getter/Setter],
        ondragstart: [Getter/Setter],
        ondrop: [Getter/Setter],
        ondurationchange: [Getter/Setter],
        onemptied: [Getter/Setter],
        onended: [Getter/Setter],
        oninput: [Getter/Setter],
        oninvalid: [Getter/Setter],
        onkeydown: [Getter/Setter],
        onkeypress: [Getter/Setter],
        onkeyup: [Getter/Setter],
        onloadeddata: [Getter/Setter],
        onloadedmetadata: [Getter/Setter],
        onloadstart: [Getter/Setter],
        onmousedown: [Getter/Setter],
        onmouseenter: [Getter/Setter],
        onmouseleave: [Getter/Setter],
        onmousemove: [Getter/Setter],
        onmouseout: [Getter/Setter],
        onmouseover: [Getter/Setter],
        onmouseup: [Getter/Setter],
        onwheel: [Getter/Setter],
        onpause: [Getter/Setter],
        onplay: [Getter/Setter],
        onplaying: [Getter/Setter],
        onprogress: [Getter/Setter],
        onratechange: [Getter/Setter],
        onreset: [Getter/Setter],
        onsecuritypolicyviolation: [Getter/Setter],
        onseeked: [Getter/Setter],
        onseeking: [Getter/Setter],
        onselect: [Getter/Setter],
        onsort: [Getter/Setter],
        onstalled: [Getter/Setter],
        onsubmit: [Getter/Setter],
        onsuspend: [Getter/Setter],
        ontimeupdate: [Getter/Setter],
        ontoggle: [Getter/Setter],
        onvolumechange: [Getter/Setter],
        onwaiting: [Getter/Setter],
        _registeredHandlers: Set {},
        _eventHandlers: [Object: null prototype] {},
        _resourceLoader: [ResourceLoader],
        _globalProxy: [Circular],
        _document: [Document],
        _sessionHistory: [SessionHistory],
        _virtualConsole: [VirtualConsole],
        _runScripts: undefined,
        _top: [Circular],
        _parent: [Circular],
        _frameElement: null,
        _length: 0,
        _pretendToBeVisual: false,
        _storageQuota: 5000000,
        _commonForOrigin: [Object],
        _currentOriginData: [Object],
        _localStorage: Storage {},
        _sessionStorage: Storage {},
        length: [Getter],
        window: [Getter],
        frameElement: [Getter],
        frames: [Getter],
        self: [Getter],
        parent: [Getter],
        top: [Getter],
        document: [Getter],
        external: [Getter],
        location: [Getter],
        history: [Getter],
        navigator: [Getter],
        locationbar: [Getter],
        menubar: [Getter],
        personalbar: [Getter],
        scrollbars: [Getter],
        statusbar: [Getter],
        toolbar: [Getter],
        performance: [Getter],
        screen: [Getter],
        localStorage: [Getter],
        sessionStorage: [Getter],
        addEventListener: [Function: bound addEventListener],
        removeEventListener: [Function: bound removeEventListener],
        dispatchEvent: [Function: bound dispatchEvent],
        setTimeout: [Function],
        setInterval: [Function],
        clearInterval: [Function: bound stopTimer],
        clearTimeout: [Function: bound stopTimer],
        __stopAllTimers: [Function],
        postMessage: [Function],
        atob: [Function],
        btoa: [Function],
        FileReader: [Function],
        WebSocket: [Function],
        AbortSignal: [Function: AbortSignal],
        AbortController: [Function: AbortController],
        XMLHttpRequest: [Function],
        ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: [Function: Int8Array],
        Uint8Array: [Function: Uint8Array],
        Uint8ClampedArray: [Function: Uint8ClampedArray],
        Int16Array: [Function: Int16Array],
        Uint16Array: [Function: Uint16Array],
        Int32Array: [Function: Int32Array],
        Uint32Array: [Function: Uint32Array],
        Float32Array: [Function: Float32Array],
        Float64Array: [Function: Float64Array],
        stop: [Function],
        close: [Function],
        getComputedStyle: [Function],
        captureEvents: [Function],
        releaseEvents: [Function],
        console: [Object],
        name: '',
        status: '',
        devicePixelRatio: 1,
        innerWidth: 1024,
        innerHeight: 768,
        outerWidth: 1024,
        outerHeight: 768,
        pageXOffset: 0,
        pageYOffset: 0,
        screenX: 0,
        screenLeft: 0,
        screenY: 0,
        screenTop: 0,
        scrollX: 0,
        scrollY: 0,
        alert: [Function],
        blur: [Function],
        confirm: [Function],
        focus: [Function],
        moveBy: [Function],
        moveTo: [Function],
        open: [Function],
        print: [Function],
        prompt: [Function],
        resizeBy: [Function],
        resizeTo: [Function],
        scroll: [Function],
        scrollBy: [Function],
        scrollTo: [Function],
        [Symbol(named property tracker)]: [NamedPropertiesTracker] },
     _documentElement:
      HTMLHtmlElementImpl {
        _eventListeners: [Object: null prototype] {},
        _ownerDocument: [Circular],
        _childNodesList: null,
        _childrenList: null,
        _version: 4,
        _memoizedQueries: {},
        _registeredObserverList: [],
        _slotableName: '',
        nodeType: 1,
        scrollTop: 0,
        scrollLeft: 0,
        _namespaceURI: 'http://www.w3.org/1999/xhtml',
        _prefix: null,
        _localName: 'html',
        _shadowRoot: null,
        _attributeList: [],
        _attributesByNameMap: Map {},
        _attributes: [NamedNodeMapImpl],
        _tabIndex: 0,
        _dataset: [DOMStringMapImpl],
        _settingCssText: false,
        _style: [CSSStyleDeclaration],
        _registeredHandlers: Set {},
        _eventHandlers: [Object: null prototype] {},
        _clickInProgress: false,
        _hasActivationBehavior: false,
        _attached: true,
        [Symbol(DOM SymbolTree)]: [SymbolTreeNode],
        [Symbol(wrapper)]: [HTMLHtmlElement] },
     _ids: [Object: null prototype] {},
     _attached: true,
     _currentScript: null,
     _pageShowingFlag: true,
     _cookieJar: CookieJar { enableLooseMode: true, store: { idx: {} } },
     _parseOptions: { sourceCodeLocationInfo: false },
     _scriptingDisabled: undefined,
     contentType: 'text/html',
     _encoding: 'UTF-8',
     _URL:
      { scheme: 'about',
        username: '',
        password: '',
        host: null,
        port: null,
        path: [Array],
        query: null,
        fragment: null,
        cannotBeABaseURL: true },
     origin: 'null',
     _location:
      LocationImpl {
        _relevantDocument: [Circular],
        url: null,
        [Symbol(wrapper)]: [Location] },
     _history:
      HistoryImpl {
        _window: [Window],
        _document: [Circular],
        _actAsIfLocationReloadCalled: [Function: actAsIfLocationReloadCalled],
        _state: null,
        [Symbol(wrapper)]: History {} },
     _workingNodeIterators: [],
     _workingNodeIteratorsMax: 10,
     _referrer: '',
     _lastModified: '01/02/2020 11:14:14',
     _asyncQueue: AsyncResourceQueue { items: Set {}, dependentItems: Set {} },
     _queue:
      ResourceQueue { paused: false, _asyncQueue: [AsyncResourceQueue], tail: null },
     _deferQueue: ResourceQueue { paused: true, _asyncQueue: undefined },
     _requestManager: RequestManager { openedRequests: [] },
     _currentDocumentReadiness: 'complete',
     _lastFocusedElement: null,
     _resourceLoader:
      PerDocumentResourceLoader {
        _document: [Circular],
        _defaultEncoding: 'UTF-8',
        _resourceLoader: [ResourceLoader],
        _requestManager: [RequestManager],
        _queue: [ResourceQueue],
        _deferQueue: [ResourceQueue],
        _asyncQueue: [AsyncResourceQueue] },
     _latestEntry: null,
     _mode: 'no-quirks',
     [Symbol(DOM SymbolTree)]:
      SymbolTreeNode {
        parent: null,
        previousSibling: null,
        nextSibling: null,
        firstChild: [DocumentTypeImpl],
        lastChild: [HTMLHtmlElementImpl],
        childrenVersion: 2,
        childIndexCachedUpTo: null,
        cachedIndex: -1,
        cachedIndexVersion: NaN },
     [Symbol(wrapper)]:
      Document {
        location: [Getter/Setter],
        [Symbol(SameObject caches)]: [Object],
        [Symbol(DOM SymbolTree)]: [SymbolTreeNode] } },
  _childNodesList: null,
  _childrenList: null,
  _version: 0,
  _memoizedQueries: {},
  _registeredObserverList: [],
  _slotableName: '',
  nodeType: 1,
  scrollTop: 0,
  scrollLeft: 0,
  _namespaceURI: 'http://www.w3.org/1999/xhtml',
  _prefix: null,
  _localName: 'img',
  _shadowRoot: null,
  _attributeList: [],
  _attributesByNameMap: Map {},
  _attributes:
   NamedNodeMapImpl { _element: [Circular], [Symbol(wrapper)]: NamedNodeMap {} },
  _tabIndex: 0,
  _dataset:
   DOMStringMapImpl { _element: [Circular], [Symbol(wrapper)]: DOMStringMap {} },
  _settingCssText: false,
  _style:
   CSSStyleDeclaration {
     _values: {},
     _importants: {},
     _length: 0,
     _onChange: [Function] },
  _registeredHandlers: Set {},
  _eventHandlers: [Object: null prototype] {},
  _clickInProgress: false,
  _hasActivationBehavior: false,
  [Symbol(DOM SymbolTree)]:
   SymbolTreeNode {
     parent: null,
     previousSibling: null,
     nextSibling: null,
     firstChild: null,
     lastChild: null,
     childrenVersion: 0,
     childIndexCachedUpTo: null,
     cachedIndex: -1,
     cachedIndexVersion: NaN },
  [Symbol(wrapper)]: HTMLImageElement {} }
asturur commented 4 years ago

Well to me it seems that you do not have node-canvas installed. Are you sure you have node-canvas correctly installed? can you render something on node?

Shohin93 commented 4 years ago

@asturur, looks like node-canvas works fine on my machine: just ran a couple of examples from https://github.com/Automattic/node-canvas/tree/master/examples and all render into files just fine.

Also tried fabric.util.getNodeCanvas(fabric.util.createImage()) both in Mac and Ubuntu (following the installation process of node-canvas from https://www.npmjs.com/package/canvas), but still, it's been returning undefined

asturur commented 4 years ago

ok i ll try to help. first thing i do i ll create an index.js to run on my machine. Maybe something is broken.

asturur commented 4 years ago

ok so the point is this.

JSDOM will instantiate a proper node-canvas backed implementation only when some atteributes are changed. In the case of the Canvas, this happens very easily, for the Image you have to change the SRC first. I have no clean solution for now that does not involve copying the JSDOM implementation code and run it arbitrarly when you use the getNodeCanvas utils.

for now i could get it work doing by setting a non usefull src property to the image. This will make mine impl.onload function run twice tho.

const { fabric } = require('fabric');
const { createWriteStream, readFileSync } = require('fs')
const imgEl2 = new fabric.util.createImage();

const out2 = createWriteStream(`${__dirname}/output2.png`);

const fileBuffer = readFileSync(`${__dirname}/test.jpg`);

// trigger a load.
imgEl2.src = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==`;

const impl = fabric.util.getNodeCanvas(imgEl2);

impl.onload = function() {
  console.log('loaded2', imgEl2.src);
  const canvas = new fabric.StaticCanvas(null, { width: 300, height: 300 });
  const fabricImage = new fabric.Image(imgEl2);
  canvas.add(fabricImage);
  canvas.renderAll();
  var stream = canvas.createPNGStream();
  stream.on('data', function(chunk) {
    out2.write(chunk);
  });
}

impl.src = fileBuffer;
asturur commented 4 years ago

i label it as possible feature. Is comfortable to use, i m not sure if we can solve it entirely from outside.

Shohin93 commented 4 years ago

Thanks a lot for all the help @asturur!

Shohin93 commented 4 years ago

@asturur one more question: could you please explain why the line const fabricImage = new fabric.Image(imgEl2); returns first the following (from fileBuffer ???):

klass {
  filters: [],
  cacheKey: 'texture0',
  _element: HTMLImageElement {},
  _originalElement: HTMLImageElement {},
  width: 225,
  height: 245 }

and then this:

klass {
  filters: [],
  cacheKey: 'texture1',
  _element: HTMLImageElement {},
  _originalElement: HTMLImageElement {},
  width: 1,
  height: 1 }

?

I understand onload is called twice, but not sure why fabric.Image(imgEl2) returns in that order. Is impl.src = fileBuffer; being read first 😕 ?

asturur commented 4 years ago

imgEl2.src has a fetcher from jsdom that is async somehow, assigning a buffer to the implementation of jsdom is ready immediately.