Export separately every step of the blend tool

Explorer ,
May 21, 2022 May 21, 2022

Copy link to clipboard

Copied

Good afternoon,

 

I want to export SEPARATELY every step of a blend between two shapes and then apply them to different pages of a book.

The request is pretty simple: I want to have one straight line evolving in a subtle way to a wavy line. To do so, I have my starting path and my arrival path. I can use the blend tool to create as many steps as I need (matching the number of pages of the book).

 

However, if I want to export, I would have to select one by one each one of those paths to then "export selection". Which is a huge lot of clicking and clicking while I am sure there is a script to fit each of those objects into a different artboard.

I am looking for such a script, that would save me a lot of time.

 

Then I will have the question on how to datamerge into an existing document that includes a main text block, but I think the biggest issue is about the Illustrator export being fastidious. 

 

Any help is welcomed.

Thank you, 

Martin Dedron

TOPICS
Draw and design , Feature request , How to , Import and export , Scripting

Views

231

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Explorer , May 24, 2022 May 24, 2022
Thank you ! I went with @m1b 's method and, with a bit of a struggle, managed to obtain what I wanted. But I will look at that for sure.

Likes

Translate

Translate
Adobe Community Professional ,
May 21, 2022 May 21, 2022

Copy link to clipboard

Copied

There is a script to export layers. You could use the Make layers function in the layers panel menu to get layers for objects.

Or you could use the Export for screens feature (but then you would need a reference object to get the size and position in the layout correctly.

 

https://github.com/TomByrne/IllustratorSmartExport

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

Thank you ! I cannot install the script (it says to use a ZXP file but there is none in the content, maybe it's because it's written in the readme that it won't be updated anymore ?)

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 21, 2022 May 21, 2022

Copy link to clipboard

Copied

As Monika said you can make layers from the blend.

Expand the blend, ungroup and use Release to Layers (Sequence)

They will become sublayers. Select all the sublayers and drag them above the main layer.

Delete the main layer and use the script.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

Thank you ! But I cannot run the script

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

I thought Monika mentioned a script, but it was a plug-in.

This script should work exporting to PDF (just make sure that your PDF export does not open Acrobat).

http://www.ericson.net/content/2011/06/export-illustrator-layers-andor-artboards-as-pngs-and-pdfs/

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 24, 2022 May 24, 2022

Copy link to clipboard

Copied

Thank you ! 

I went with @m1b 's method and, with a bit of a struggle, managed to obtain what I wanted. But I will look at that for sure.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 21, 2022 May 21, 2022

Copy link to clipboard

Copied

Hi @MartinDedron, sounds like a fun project. 🙂 Is your book going to be made in Illustrator or Indesign?

- Mark

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 22, 2022 May 22, 2022

Copy link to clipboard

Copied

Hi @MartinDedron, I had an idea for a script that might really help your project.

 

To use it, first start with a blend and expand it then ungroup it and keep all the paths selected. Then run my script. It will create a new illustrator document, then create new artboards that match the size of the artboard in the original document, and then duplicate each selected page item to the new document, positioned on the corresponding artboard.

- Mark

 

Here's the script—everything really happens in the first function and the rest of the functions are just little utility functions that make things convenient.

 

/*
    Duplicate Items To New Doc Artboards.js
    for Adobe Illustrator 2022

    by m1b
    here: https://community.adobe.com/t5/illustrator-discussions/export-separately-every-step-of-the-blend-tool/m-p/12956749

*/


duplicateItemsToNewDocArtboards(app.activeDocument.selection, 10, -1);


/**
 * duplicateItemsToNewDocArtboards
 * creates a new document and then
 * duplicates each item to a new
 * artboard on that document
 * @param {Array[PageItem]} items - the page items to duplicate
 * @param {[Number]} gap - spacing in points between artboards
 */
function duplicateItemsToNewDocArtboards(items, gap, direction) {
    if (items == undefined)
        return;

    gap = gap || 0;
    direction = direction || 1;

    var masterArtboard = getArtboardsOfItem(items[0])[0];

    if (masterArtboard == undefined)
        masterArtboard = getParentDocument(items[0]).artboards[0];

    var newArtboards = [],
        sideLength = Math.ceil(Math.sqrt(items.length)),
        w = masterArtboard.artboardRect[2] - masterArtboard.artboardRect[0],
        h = -(masterArtboard.artboardRect[3] - masterArtboard.artboardRect[1]),
        tx = masterArtboard.artboardRect[0],
        ty = masterArtboard.artboardRect[1],
        dx = (w + gap) * sideLength / 2,
        dy = (h + gap) * sideLength / 2,
        newDoc = app.documents.add();

    if (direction == -1) {
        reversedItems = [];
        for (var i = items.length - 1; i >= 0; i--)
            reversedItems.push(items[i]);
        items = reversedItems;
    }

    for (var i = 0; i < items.length; i++) {
        var x = i % sideLength,
            y = Math.floor(i / sideLength),
            xpos = (w + gap) * x - dx,
            ypos = (h + gap) * y - dy;

        newArtboards[i] = newDoc.artboards.add([xpos, -ypos, xpos + w, -(ypos + h)]);

        var dup = items[i].duplicate(newDoc, ElementPlacement.PLACEATBEGINNING);
        dup.position = [items[i].left + xpos - tx, items[i].top - ypos - ty];
    }
    newDoc.artboards[0].remove();
}



/**
 * getArtboardsOfItem
 * returns array of artboards that intersect the item
 * @param {PageItem} item - a page item
 * @param {[Array[Artboard]]} _artboards - artboards to search, if undefined, search all
 * @param {[Boolean]} preferIndex - if true, returns array of artboard indices {Array[Number]}
 * @returns {Array[Artboard]} - array of artboards
 */
function getArtboardsOfItem(item, _artboards, preferIndex) {
    var doc = getParentDocument(item);
    _artboards = _artboards || doc.artboards;
    var bounds = getItemBounds(item);
    var result = [];
    for (var i = 0; i < _artboards.length; i++) {
        if (boundsDoIntersect(bounds, _artboards[i].artboardRect))
            result.push(preferIndex
                ? i
                : _artboards[i]
            );
    }
    return result
}



/**
 * getItemBounds
 * attempts to calculate items bounds
 * including correct bounds of clipped group
 * @param {PageItem} item - a page item
 * @param {Boolean} geometric - false = visibleBounds, true = geometricBounds
 * @param {Array[4]} bounds - [l,t,r,b]
 */
function getItemBounds(item, geometric, bounds) {
    if (item == undefined) return;
    var newBounds = [];
    if (item.typename == 'GroupItem') {

        var children = item.pageItems,
            contentBounds = [];
        if (item.hasOwnProperty('clipped') && item.clipped == true) {
            // item is clipping group
            var clipBounds;
            for (var i = 0; i < children.length; i++) {
                var child = children[i];
                if (child.hasOwnProperty('clipping') && child.clipping == true) {
                    // the clipping item
                    clipBounds = child.geometricBounds;
                } else {
                    // a clipped content item
                    var b = expandBounds(getItemBounds(child, geometric, bounds), contentBounds);
                }
            }
            if (clipBounds != undefined)
                newBounds = intersectionOfBounds([clipBounds, contentBounds]);
            else
                newBounds = contentBounds;
        }

        else {
            // item is a normal group
            for (var i = 0; i < children.length; i++) {
                var b = expandBounds(getItemBounds(children[i], geometric, bounds), contentBounds);
            }
            newBounds = contentBounds;
        }
    } else {
        // item is not clipping group
        newBounds = geometric ? item.geometricBounds : item.visibleBounds;
    }
    if (bounds == undefined) {
        bounds = newBounds;
    } else {
        bounds = expandBounds(newBounds, bounds);
    }
    return bounds;
}



/**
 * expandBounds
 * returns bounds that encompass two bounds
 * @param {Array[4]} b1 - bounds [l,t,r,b]
 * @param {Array[4]} b2 - bounds [l,t,r,b]
 * @returns {Array[4]} [l,t,b,r]
 */
function expandBounds(b1, b2) {
    var expanded = b2;
    for (var i = 0; i < 4; i++) {
        if (b1[i] != undefined && b2[i] == undefined) expanded[i] = b1[i];
        if (b1[i] == undefined && b2[i] != undefined) expanded[i] = b2[i];
        if (b1[i] == undefined && b2[i] == undefined) return;
    }
    if (b1[0] < b2[0]) expanded[0] = b1[0];
    if (b1[1] > b2[1]) expanded[1] = b1[1];
    if (b1[2] > b2[2]) expanded[2] = b1[2];
    if (b1[3] < b2[3]) expanded[3] = b1[3];
    return expanded;
}


/**
 * intersectionOfBounds
 * calculates intersecting rectangle
 * @param {Array[Array[4]]} arrayOfBounds - array of [l,t,b,r] arrays
 * @returns {Array[4]} [l,t,b,r]
 */
function intersectionOfBounds(arrayOfBounds) {
    bounds = arrayOfBounds.slice(0).sort(function (a, b) { return b[0] - a[0] || a[1] - b[1] });
    var intersection = bounds.shift();
    while (b = bounds.shift()) {
        if (!boundsDoIntersect(intersection, b)) return;
        var l = Math.max(intersection[0], b[0]),
            t = Math.min(intersection[1], b[1]),
            r = Math.min(intersection[2], b[2]),
            b = Math.max(intersection[3], b[3]);
        intersection = [l, t, r, b];
    }
    return intersection;
}

/**
 * boundsDoIntersect
 * returns true when bounds intersect
 * @param {Array[4]} b1 - bounds [l,t,r,b]
 * @param {Array[4]} b2 - bounds [l,t,r,b]
 * @returns {boolean}
 */
function boundsDoIntersect(b1, b2) {
    return !(
        b2[0] > b1[2] ||
        b2[2] < b1[0] ||
        b2[1] < b1[3] ||
        b2[3] > b1[1]
    );
}


/**
 * getParentDocument
 * @param {PageItem} obj - a page item
 * @returns {Document} the page item's document
 */
function getParentDocument(obj) {
    while (obj.hasOwnProperty('parent') && obj.constructor.name != 'Document')
        obj = obj.parent;
    return obj;
}

 

EDIT: fixed positioning bug and removed error when first item isn't on any artboard.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

Thank you !

 

As I use graphics and photographs, Illustrator and Photoshop are part of the project but it is mainly being done in InDesign.

 

I have a problem with your script: it doesn't fit the content to the artboards and it seems that there are some artboards missing ? 😕

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

though it worked one time, for my second path it says rthat there is an error on this line: 

w = masterArtboard.artboardRect[2] - masterArtboard.artboardRect[0],
        h = -(masterArtboard.artboardRect[3] - masterArtboard.artboardRect

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

If you want a picture example on how it doesn't match the artboards: 

Snag_4abb513.png

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 23, 2022 May 23, 2022

Copy link to clipboard

Copied

Hi @MartinDedron, thanks for trying it out. The script doesn't attempt to "fit the content to the artboards"... it (should) match the spacial relationship of the page item to the master artboard. Also when I wrote the script I assumed (haha) that the paths would be completely on the artboard—if they aren't, then it raises more problems such as (a) not being able to get the artboard dimensions in the first place (because item might not be on an artboard at all!) and (b) the final object falling mostly on a wrong artboard due to my layout scheme. To fix this I'll need to put them each in a clipping mask—I can add that.

 

If you think this might still be a promising approach for you, please send me one or two sample .ai files that currently fail and I'll bounce off those.

 

Once it's working, I'm imagining a nice setup could be to export the multi-artboard ai document as multi-page pdf and import into indesign like that, but using a little script to match each page to the pages of the indesign doc.

- Mark

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 24, 2022 May 24, 2022

Copy link to clipboard

Copied

Hi Mark !

 

Well as I'm in a rush I went full-on "learning by trying", and I noticed that if all the shapes exactly fit one artboard (which isn't what I thought was obvious: I tried to create a "destination artboard", having the exact size that I wanted to furtherly use), clipping them will be ok.

Then, I put the keyboard increments to a really low number and I place them manually. Some might overlap on two so, a visual check is needed.

Afterwards I datamerged them and it finally went "ok", I just had trouble with Indesign's functionment (I created all my pages but if I fill the main text block, the "intelligent" text distribution just creates a page BEFORE my edited pages, hum).

 

Now with some energy I nailed everything. Thanks again !

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 24, 2022 May 24, 2022

Copy link to clipboard

Copied

That's awesome @MartinDedron good to hear you got it done. I'm not sure I follow your solution perfectly but it sounds epic. 🙂

 

For next time learning... there's a feature of my conception of your problem and my solution that I didn't make clear earlier—that it avoids any manual positioning in the final files. It can do this by: 1. making the master artboard  have a sensible relationship with page size, eg. the same size, and 2. page items will be in the correct position on the master artboard such that they will be in the correct position on the final page. This means that the blended items must be stacked, not spread out (unless they should be spread out on the final pages!). If those 2 conditions are met, when they are datamerged or imported they will be in the correct position already. Also this would be a great time to add an Object Style to the frames in Indesign so that the position can be changed for all of the "frames" at once by editing the Object Style.

- Mark

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
May 26, 2022 May 26, 2022

Copy link to clipboard

Copied

Thank you.

I'm not sure I understand to be true:

1. I tried the master artboard having a. the same size as the shape to be exported ; b. the master artboard having the same size as the whole compound shape ; none of them resulted in these being placed automatically.

2. What do you mean by stacked and spread out ? On Illustrator I decomposed then ungrouped the object. I'm not sure what you mean.

 

Have a good day,

Martin

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 29, 2022 May 29, 2022

Copy link to clipboard

Copied

Hi @MartinDedron, here's what I was imagining:

 

1. I make the blend on an artboard that matches the page size of the document (click to enlarge).

dup-items-to-pages-2.png   dup-items-to-pages-3.png

2. I expand and ungroup the blend so there are separate path items selected.

3. I run my script.

 

For showing the idea I have added a text frame with dummy text:

dup-items-to-pages-1.png

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 31, 2022 May 31, 2022

Copy link to clipboard

Copied

Hi @MartinDedron, I just noticed that my script posted has a major bug which is almost certainly the reason it wasn't working sensibly for you. I have updated the code above, so if you need it again, give it a try. Sorry about that. Bugs often occur only in certain situations and that was the case here.

- Mark

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 01, 2022 Jun 01, 2022

Copy link to clipboard

Copied

Thank you 🙂
I haven't had the time to try it yet, but because of my way of thinking, I totally didn't think about doing it that way.

Indeed, I thought : "If I have four shapes but two are the symetry of the two other ones, therefore I have two shapes".

See linked file.

Henceforth I scaled my artboard size regarding the container in which they were supposed to fit. Having four on the same page, I also didn't (and still don't) know if it would work well with 4*100 steps (or more).

 

The main issue I had wasn't to refit them on the artboard (it's fastidious but I've done way worse), it is that

1. they slightly move inside of the artboard. It could be awesome to set a reference point in the "filet", the line. Indeed, the wave shape makes it move as the "middle" changes position ;

2. InDesign has a "what the hell!" way of using its datamerge !! Grrr. If you prepare your book with a main text block, pages number, etc. And datamerge those shapes inside of it, the automatic text fill won't fill the pages that were created with the right shapes : it will add pages BEFORE them. What a pleasure. Therefore you have either to copy and paste the shapes page after page (which I think is longer), or to chain the text blocks manually (which is ok for a hundred pages, but I wouldn't do it for a whole collection).

 

I will try to run the script again in a few days, thanks for the correction! If you have any idea regarding what could make the datamerge + positionning of those shapes more accurate and easy to use, please feel free to let me know.

 

Martin Dedron

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Jun 01, 2022 Jun 01, 2022

Copy link to clipboard

Copied

LATEST

Note to readers: my post here is now about Indesign not Illustrator.

 

Okay @MartinDedron your example page helps. It's an interesting exercise. I'm assuming that your document uses facing pages (if not, the solution is just slightly different than what I am about to post).

 

I've written a script for Indesign that will import a multiple page pdf with one page of pdf per page of indesign document, with the option of duplicating and/or flipping the same page to the right-hand-page. To make the pdf ready to import into indesign, follow my instructions I posted earlier in Illustrator, using my script, specifically making sure the blended items are on artboards that match the indesign document page size and in the correct positions on the artboards.

blend-anim.gif  Screen Shot 2022-06-02 at 1.28.58 pm.png

Then save that output .ai document as .pdf (all artboards). Then open or make a document with correct dimensions and number of pages and run script below "Place Multipage PDF On Multiple Pages.js". Script will ask for pdf file so select the one you just saved from Illustrator.

 

To see it working, see the demo sample files attached.

- Mark

blend-indd-anim.gif

 

/*

    Place Multipage PDF on Multiple Pages.js
    for Adobe Indesign

    by m1b
    discussion here: https://community.adobe.com/t5/illustrator-discussions/export-separately-every-step-of-the-blend-tool/m-p/12978097

    Imports a multi-page pdf into indesign, putting
    one page of pdf on each page of indesign document
    aligning at top left corner. (Ideal for when pdf
    dimensions match indesign document dimensions.)

*/

var settings = {

    // the image file to place (assumes is multiple pages pdf)
    // leave undefined for user to choose pdf
    // imageFile: new File('/Users/foobar/Desktop/blend-split-output.pdf'),

    // layer name on which to place
    // leave undefined to use active layer
    // layerName: 'blend',

    // place first page of image on page with this documentOffset
    // 0 is first page in document
    startDocumentOffset: 0,

    // will place the same image on the facing page
    repeatToFacingPage: true,

    // will place the same image on the facing page flipped horizontally
    flipOnRightHandPage: true,

};


function main() {

    if (settings.imageFile == undefined) {
        settings.imageFile = chooseFile('pdf');
        if (settings.imageFile == undefined)
            return;
    }

    var doc = app.activeDocument,
        documentOffset = settings.startDocumentOffset,
        firstImagePageNumber = 1,
        imageFile = settings.imageFile;

    if (!imageFile.exists) {
        alert('File "' + imageFile + '" doesn\'t exist.');
        return;
    };

    var targetLayer;
    if (settings.layerName != undefined) {
        targetLayer = doc.layers.itemByName(settings.layerName)
        if (!targetLayer.isValid)
            targetLayer = undefined;
    }

    // Explr.init(app.pdfPlacePreferences); return;
    app.pdfPlacePreferences.pdfCrop = PDFCrop.CROP_MEDIA;

    var finishedPlacingEveryPage = false,
        docPageCounter = 0,
        imagePageCounter = 0;

    while (!finishedPlacingEveryPage) {

        var page = doc.pages[documentOffset + docPageCounter];
        app.pdfPlacePreferences.pageNumber = firstImagePageNumber + imagePageCounter;

        if (!page.isValid)
            break;

        var placedImage = page.place(/* file */ imageFile, /* placePoint */[0, 0], /* destinationLayer */ targetLayer, /* showingOptions */ false, /* autoflowing */ false)[0];

        if (
            imagePageCounter > 0
            && placedImage.pdfAttributes.pageNumber == firstImagePageNumber
        ) {
            placedImage.parent.remove();
            finishedPlacingEveryPage = true;
            continue;
        };

        // repeat to right hand page?
        if (
            settings.repeatToFacingPage == true
            && page.side == PageSideOptions.LEFT_HAND
        ) {
            var facingPage = doc.pages[documentOffset + docPageCounter + 1];
            if (facingPage.isValid) {
                var repeatImage = placedImage.duplicate(facingPage);
                docPageCounter++;

                if (settings.flipOnRightHandPage == true)
                    repeatImage.flipItem(Flip.HORIZONTAL, AnchorPoint.CENTER_ANCHOR);
            }

        }

        // flip image if necessary on right hand page
        else if (
            page.side == PageSideOptions.RIGHT_HAND
            && settings.flipOnRightHandPage == true
        ) {
            placedImage.flipItem(Flip.HORIZONTAL, AnchorPoint.CENTER_ANCHOR);
        }

        docPageCounter++;
        imagePageCounter++;

    };

} // end main

function chooseFile(fileExtension) {
    fileExtension == fileExtension || 'txt';
    fileExtension = fileExtension.replace('.', '');

    var f, fsFilter;
    if ($.os.match("Windows")) {
        fsFilter = "*." + fileExtension;
    } else {
        var regex = RegExp('\.' + fileExtension, 'i');
        fsFilter = function (f) { return (regex.test(f.name) || f.constructor.name == 'Folder') };
    };

    f = File.openDialog("Please select a '" + fileExtension + "' File", fsFilter, false);

    if (f != undefined && f.exists) return f;
}

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Place Multipage PDF');

 

P.S. Something that can make it hard to align graphics in indesign is when they aren't using the artboard for cropping. When importing a graphic to Indesign, click "Show options" in the Place dialog and change the bounding box to, for example, Trim or Bleed. This will respect the artboard sizes in the pdf and make positioning easy. The script does this, but you can do it anytime you are importing a graphic.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines