├── .all-contributorsrc ├── .coveragerc ├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── bumpversion.yml │ ├── import-test.yml │ ├── issue-translator.yml │ ├── publish_docs.yml │ ├── python-publish.yml │ └── pythonpackage.yml ├── .gitignore ├── .hound.yml ├── .pre-commit-config.yaml ├── .pylintrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── changelog.md ├── gen_api_nav.py ├── gen_examples.py ├── index.md └── requirements.txt ├── examples ├── _psd_files.py ├── active_layer.py ├── add_metadata.py ├── add_slate.py ├── add_start_application_event.py ├── apply_crystallize_filter_action.py ├── apply_filters.py ├── change_color_of_background_and_foreground.py ├── color.py ├── compare_colors.py ├── convert_smartobject_to_layer.py ├── copy_and_paste.py ├── create_new_document.py ├── create_thumbnail.py ├── creating_a_layer.py ├── cropping.py ├── current_tool.py ├── delete_and_fill_selection.py ├── do_photoshop_action.py ├── emboss_action.py ├── enable_generator.py ├── eval_javascript.py ├── export_artboards.py ├── export_document.py ├── export_document_with_options.py ├── export_layers_as_png.py ├── export_layers_use_export_options_saveforweb.py ├── files │ ├── artboard_example.psd │ ├── export_layers_as_png.psd │ ├── green_130x260.png │ ├── layer_comps.psd │ ├── red_100x200.png │ ├── replace_images.psd │ ├── slate_template.psd │ └── trim.psd ├── fill_selection.py ├── fit_on_screen.py ├── get_document_by_name.py ├── get_layer_by_name.py ├── hello_world.py ├── import_image_as_layer.py ├── link_layer.py ├── list_documents.py ├── load_selection.py ├── move_to_end.py ├── new_document.py ├── open_psd.py ├── operate_channels.py ├── operate_layerSet.py ├── photoshop_session.py ├── replace_images.py ├── revert_changes.py ├── rotate_layer.py ├── run_batch.py ├── save_as_pdf.py ├── save_as_tga.py ├── save_to_psd.py ├── selection_stroke.py ├── session_callback.py ├── session_document_duplicate.py ├── session_hello_world.py ├── session_new_document.py ├── session_smart_sharpen.py ├── set_active_layer.py ├── smart_sharpen.py ├── toggle_proof_colors.py └── trim.py ├── mkdocs.yml ├── photoshop ├── __init__.py ├── __version__.py ├── api │ ├── __init__.py │ ├── _active_layer.py │ ├── _artlayer.py │ ├── _artlayers.py │ ├── _channel.py │ ├── _channels.py │ ├── _core.py │ ├── _document.py │ ├── _documentinfo.py │ ├── _documents.py │ ├── _layerComp.py │ ├── _layerComps.py │ ├── _layerSet.py │ ├── _layerSets.py │ ├── _layers.py │ ├── _measurement_log.py │ ├── _notifier.py │ ├── _notifiers.py │ ├── _preferences.py │ ├── _selection.py │ ├── _text_fonts.py │ ├── action_descriptor.py │ ├── action_list.py │ ├── action_reference.py │ ├── application.py │ ├── batch_options.py │ ├── colors │ │ ├── __init__.py │ │ ├── cmyk.py │ │ ├── gray.py │ │ ├── hsb.py │ │ ├── lab.py │ │ └── rgb.py │ ├── constants.py │ ├── enumerations.py │ ├── errors.py │ ├── event_id.py │ ├── open_options │ │ ├── __init__.py │ │ └── eps.py │ ├── save_options │ │ ├── __init__.py │ │ ├── bmp.py │ │ ├── eps.py │ │ ├── gif.py │ │ ├── jpg.py │ │ ├── pdf.py │ │ ├── png.py │ │ ├── psd.py │ │ ├── tag.py │ │ └── tif.py │ ├── solid_color.py │ ├── text_font.py │ └── text_item.py └── session.py ├── poetry.lock ├── pyproject.toml ├── renovate.json └── test ├── conftest.py ├── manual_test ├── manual_test_all_examples.py ├── manual_test_application.py ├── manual_test_layer_comps.py ├── manual_test_solid_color.py └── manual_test_text_item.py ├── test_data ├── layer_comps.psd └── textitem.psd └── test_imports.py /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "photoshop-python-api", 3 | "projectOwner": "loonghao", 4 | "repoType": "github", 5 | "commitConvention": "angular", 6 | "repoHost": "https://github.com", 7 | "files": [ 8 | "README.md" 9 | ], 10 | "imageSize": 100, 11 | "commit": true, 12 | "contributorsPerLine": 7, 13 | "contributors": [ 14 | { 15 | "login": "loonghao", 16 | "name": "Hal", 17 | "avatar_url": "https://avatars1.githubusercontent.com/u/13111745?v=4", 18 | "profile": "https://github.com/loonghao", 19 | "contributions": [ 20 | "code" 21 | ] 22 | }, 23 | { 24 | "login": "voodraizer", 25 | "name": "voodraizer", 26 | "avatar_url": "https://avatars0.githubusercontent.com/u/1332729?v=4", 27 | "profile": "https://github.com/voodraizer", 28 | "contributions": [ 29 | "bug" 30 | ] 31 | }, 32 | { 33 | "login": "brunosly", 34 | "name": "brunosly", 35 | "avatar_url": "https://avatars2.githubusercontent.com/u/4326547?v=4", 36 | "profile": "https://github.com/brunosly", 37 | "contributions": [ 38 | "bug" 39 | ] 40 | }, 41 | { 42 | "login": "tubi-carrillo", 43 | "name": "tubi", 44 | "avatar_url": "https://avatars3.githubusercontent.com/u/33004093?v=4", 45 | "profile": "https://github.com/tubi-carrillo", 46 | "contributions": [ 47 | "bug" 48 | ] 49 | }, 50 | { 51 | "login": "wjxiehaixin", 52 | "name": "wjxiehaixin", 53 | "avatar_url": "https://avatars0.githubusercontent.com/u/48039822?v=4", 54 | "profile": "https://github.com/wjxiehaixin", 55 | "contributions": [ 56 | "bug" 57 | ] 58 | }, 59 | { 60 | "login": "enzozhong", 61 | "name": "罗马钟", 62 | "avatar_url": "https://avatars0.githubusercontent.com/u/993544?v=4", 63 | "profile": "http://it.econline.net", 64 | "contributions": [ 65 | "bug" 66 | ] 67 | }, 68 | { 69 | "login": "ClementHector", 70 | "name": "clement", 71 | "avatar_url": "https://avatars.githubusercontent.com/u/7068597?v=4", 72 | "profile": "https://github.com/ClementHector", 73 | "contributions": [ 74 | "bug" 75 | ] 76 | }, 77 | { 78 | "login": "krevlinmen", 79 | "name": "krevlinmen", 80 | "avatar_url": "https://avatars.githubusercontent.com/u/56278440?v=4", 81 | "profile": "https://github.com/krevlinmen", 82 | "contributions": [ 83 | "bug" 84 | ] 85 | }, 86 | { 87 | "login": "SThomasN", 88 | "name": "Thomas", 89 | "avatar_url": "https://avatars.githubusercontent.com/u/63218023?v=4", 90 | "profile": "https://github.com/SThomasN", 91 | "contributions": [ 92 | "bug" 93 | ] 94 | }, 95 | { 96 | "login": "CaptainCsaba", 97 | "name": "CaptainCsaba", 98 | "avatar_url": "https://avatars.githubusercontent.com/u/59013751?v=4", 99 | "profile": "https://github.com/CaptainCsaba", 100 | "contributions": [ 101 | "bug" 102 | ] 103 | }, 104 | { 105 | "login": "Afanyiyu", 106 | "name": "Il Harper", 107 | "avatar_url": "https://avatars.githubusercontent.com/u/20179549?v=4", 108 | "profile": "https://ilharper.vbox.moe", 109 | "contributions": [ 110 | "code" 111 | ] 112 | }, 113 | { 114 | "login": "blunderedbishop", 115 | "name": "blunderedbishop", 116 | "avatar_url": "https://avatars.githubusercontent.com/u/56189376?v=4", 117 | "profile": "https://github.com/blunderedbishop", 118 | "contributions": [ 119 | "bug" 120 | ] 121 | }, 122 | { 123 | "login": "MrTeferi", 124 | "name": "MrTeferi", 125 | "avatar_url": "https://avatars.githubusercontent.com/u/92750180?v=4", 126 | "profile": "https://github.com/MrTeferi", 127 | "contributions": [ 128 | "code" 129 | ] 130 | }, 131 | { 132 | "login": "damienchambe", 133 | "name": "Damien Chambe", 134 | "avatar_url": "https://avatars.githubusercontent.com/u/42462209?v=4", 135 | "profile": "https://github.com/damienchambe", 136 | "contributions": [ 137 | "code" 138 | ] 139 | }, 140 | { 141 | "login": "be42day", 142 | "name": "Ehsan Akbari Tabar", 143 | "avatar_url": "https://avatars.githubusercontent.com/u/20614168?v=4", 144 | "profile": "https://github.com/be42day", 145 | "contributions": [ 146 | "bug" 147 | ] 148 | }, 149 | { 150 | "login": "Alyxion", 151 | "name": "Michael Ikemann", 152 | "avatar_url": "https://avatars.githubusercontent.com/u/33489959?v=4", 153 | "profile": "http://www.linkedin.com/in/michael-ikemann", 154 | "contributions": [ 155 | "bug" 156 | ] 157 | }, 158 | { 159 | "login": "dsmtE", 160 | "name": "Enguerrand DE SMET", 161 | "avatar_url": "https://avatars.githubusercontent.com/u/37016704?v=4", 162 | "profile": "https://github.com/dsmtE", 163 | "contributions": [ 164 | "code" 165 | ] 166 | }, 167 | { 168 | "login": "feisuzhu", 169 | "name": "Proton", 170 | "avatar_url": "https://avatars.githubusercontent.com/u/857880?v=4", 171 | "profile": "http://www.thbattle.net", 172 | "contributions": [ 173 | "code" 174 | ] 175 | } 176 | ], 177 | "skipCi": true, 178 | "commitType": "docs" 179 | } 180 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = photoshop 4 | 5 | [report] 6 | exclude_lines = 7 | if self.debug: 8 | pragma: no cover 9 | raise NotImplementedError 10 | if __name__ == .__main__.: 11 | ignore_errors = True 12 | omit = 13 | test/* 14 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = BLK100 3 | 4 | # flake8-quotes: 5 | # Use double quotes as our default to comply with black, we like it and 6 | # don't want to use single quotes anymore. 7 | # We would love to configure this via our pyproject.toml but flake8-3.8 does 8 | # not support it yet. 9 | inline-quotes = double 10 | multiline-quotes = double 11 | docstring-quotes = double 12 | avoid-escape = True 13 | 14 | # flake8-docstrings 15 | # Use the Google Python Styleguide Docstring format. 16 | docstring-convention = google 17 | 18 | exclude = 19 | .git, 20 | __pycache__, 21 | docs/source/conf.py, 22 | old, 23 | build, 24 | dist, 25 | venv, 26 | docs, 27 | examples, 28 | test 29 | 30 | max-line-length = 120 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows10, Windows7] 28 | - Photoshop Version: [e.g. Photoshop-2020] 29 | - Python Version: [e.g. python-3.6] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/bumpversion.yml: -------------------------------------------------------------------------------- 1 | name: Bump version 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | bump-version: 10 | if: "!startsWith(github.event.head_commit.message, 'bump:')" 11 | runs-on: ubuntu-latest 12 | name: "Bump version and create changelog with commitizen" 13 | steps: 14 | - name: Check out 15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 16 | with: 17 | fetch-depth: 0 18 | token: '${{ secrets.PERSONAL_ACCESS_TOKEN }}' 19 | - name: Create bump and changelog 20 | uses: commitizen-tools/commitizen-action@master 21 | with: 22 | github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 23 | branch: main 24 | -------------------------------------------------------------------------------- /.github/workflows/import-test.yml: -------------------------------------------------------------------------------- 1 | name: Import Test 2 | on: [pull_request] 3 | 4 | jobs: 5 | python-check: 6 | runs-on: windows-2022 7 | strategy: 8 | max-parallel: 3 9 | matrix: 10 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 11 | 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install -U pip poetry 23 | poetry --version 24 | poetry install -vvv || poetry install -vvv || poetry install -vvv 25 | poetry run pytest 26 | -------------------------------------------------------------------------------- /.github/workflows/issue-translator.yml: -------------------------------------------------------------------------------- 1 | name: 'issue-translator' 2 | on: 3 | issue_comment: 4 | types: [created] 5 | issues: 6 | types: [opened] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: usthe/issues-translate-action@v2.7 13 | with: 14 | IS_MODIFY_TITLE: false 15 | # not require, default false, . Decide whether to modify the issue title 16 | # if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot. 17 | CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿 18 | # not require. Customize the translation robot prefix message. 19 | -------------------------------------------------------------------------------- /.github/workflows/publish_docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | docs: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | id-token: write 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: '3.10' 23 | 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install poetry 28 | poetry install 29 | 30 | - name: Setup Git User 31 | run: | 32 | git config --global user.name "github-actions[bot]" 33 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 34 | 35 | - name: Build and Deploy Documentation 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | run: | 39 | poetry run mkdocs gh-deploy --force --remote-branch gh-pages 40 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | # IMPORTANT: this permission is mandatory for trusted publishing 13 | id-token: write 14 | contents: write 15 | 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 18 | with: 19 | token: "${{ secrets.GITHUB_TOKEN }}" 20 | fetch-depth: 0 21 | ref: main 22 | - uses: olegtarasov/get-tag@v2.1.4 23 | id: get_tag_name 24 | with: 25 | tagRegex: "v(?.*)" 26 | - name: Set up Python 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.x' 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install -U pip poetry mkdocs mkdocs-material 33 | poetry --version 34 | poetry build 35 | # Note that we don't need credentials. 36 | # We rely on https://docs.pypi.org/trusted-publishers/. 37 | - name: Upload to PyPI 38 | uses: pypa/gh-action-pypi-publish@release/v1 39 | with: 40 | packages-dir: dist 41 | - name: Generate changelog 42 | id: changelog 43 | uses: jaywcjlove/changelog-generator@main 44 | with: 45 | token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 46 | filter-author: (|dependabot|renovate\\[bot\\]|dependabot\\[bot\\]|Renovate Bot) 47 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 48 | template: | 49 | ## Bugs 50 | {{fix}} 51 | ## Feature 52 | {{feat}} 53 | ## Improve 54 | {{refactor,perf,clean}} 55 | ## Misc 56 | {{chore,style,ci||🔶 Nothing change}} 57 | ## Unknown 58 | {{__unknown__}} 59 | - uses: ncipollo/release-action@v1 60 | with: 61 | artifacts: "dist/*.whl" 62 | token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 63 | body: | 64 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }} 65 | 66 | ${{ steps.changelog.outputs.changelog }} 67 | -------------------------------------------------------------------------------- /.github/workflows/pythonpackage.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | on: [pull_request] 3 | 4 | jobs: 5 | python-check: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | max-parallel: 3 9 | matrix: 10 | python-version: ["3.8", "3.9", "3.10"] 11 | 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install -U pip poetry 23 | poetry --version 24 | poetry install 25 | 26 | - name: Run tests and linters 27 | run: | 28 | #!/bin/sh -e 29 | git config --global user.email "action@github.com" 30 | git config --global user.name "GitHub Action" 31 | export PREFIX="poetry run python -m " 32 | if [ -d 'venv' ] ; then 33 | export PREFIX="venv/bin/" 34 | fi 35 | 36 | ${PREFIX}black photoshop --check 37 | ${PREFIX}isort --check-only photoshop 38 | ${PREFIX}flake8 photoshop --max-line-length 120 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | *.py[cod] 3 | 4 | # PyCharm project files 5 | .idea/ 6 | 7 | # Vim / Notepad++ temp files 8 | *.egg-info 9 | # PyInstaller output 10 | build/ 11 | dist/ 12 | *.spec 13 | 14 | # PyBuilder output 15 | .build/ 16 | result/ 17 | 18 | # Coverage output 19 | .coverage 20 | /venv/ 21 | venv_python 22 | 23 | # Docs 24 | docs_src/_build/ 25 | /.windsurfrules 26 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | python: 2 | enabled: true 3 | 4 | flake8: 5 | enabled: true 6 | config_file: .flake8 7 | 8 | fail_on_violations: true 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: conf.py 2 | repos: 3 | - repo: https://github.com/ambv/black 4 | rev: 22.12.0 5 | hooks: 6 | - id: black 7 | language_version: python3.10 8 | - hooks: 9 | - id: trailing-whitespace 10 | - id: end-of-file-fixer 11 | - id: check-docstring-first 12 | - id: check-json 13 | - id: check-yaml 14 | - id: debug-statements 15 | - id: name-tests-test 16 | - id: requirements-txt-fixer 17 | repo: https://github.com/pre-commit/pre-commit-hooks 18 | rev: v2.4.0 19 | - hooks: 20 | - additional_dependencies: 21 | - flake8-typing-imports==1.5.0 22 | id: flake8 23 | repo: https://github.com/pycqa/flake8 24 | rev: 3.7.9 25 | - hooks: 26 | - id: autopep8 27 | repo: https://github.com/pre-commit/mirrors-autopep8 28 | rev: v1.4.4 29 | - hooks: 30 | - id: validate_manifest 31 | repo: https://github.com/pre-commit/pre-commit 32 | rev: v1.21.0 33 | - hooks: 34 | - args: 35 | - --py36-plus 36 | id: pyupgrade 37 | repo: https://github.com/asottile/pyupgrade 38 | rev: v1.25.3 39 | - hooks: 40 | - args: 41 | - --py3-plus 42 | id: reorder-python-imports 43 | repo: https://github.com/asottile/reorder_python_imports 44 | rev: v1.9.0 45 | - hooks: 46 | - args: 47 | - --py36-plus 48 | id: add-trailing-comma 49 | repo: https://github.com/asottile/add-trailing-comma 50 | rev: v1.5.0 51 | - hooks: 52 | - id: commitizen 53 | stages: 54 | - commit-msg 55 | repo: https://github.com/commitizen-tools/commitizen 56 | rev: v2.17.8 57 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | # Generated Pylint configuration file that disables default output tables. 2 | 3 | [MESSAGES CONTROL] 4 | disable=RP0001,RP0002,RP0003,RP0101,RP0401,RP0402,RP0701,RP0801,C0103,R0903 5 | 6 | [REPORTS] 7 | output-format=text 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.8" 4 | - "3.9" 5 | - "3.10" 6 | before_script: 7 | - pip install poetry 8 | - poetry install 9 | script: 10 | - poetry run codecov 11 | 12 | cache: pip 13 | before_cache: 14 | - rm -f $HOME/.cache/pip/log/debug.log 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.24.1 (2025-05-07) 2 | 3 | ### Fix 4 | 5 | - **deps**: update dependency comtypes to v1.4.10 6 | 7 | ## v0.24.0 (2024-12-29) 8 | 9 | ### Feat 10 | 11 | - **examples**: add scripts for deleting and filling selections and exporting documents with options 12 | - **artlayer**: add convertToSmartObject method and update layer kind property 13 | 14 | ### Refactor 15 | 16 | - **artlayer**: remove unused import of LayerKind enumeration 17 | 18 | ## v0.23.0 (2024-12-29) 19 | 20 | ### Feat 21 | 22 | - **examples**: enhance documentation and examples for Photoshop scripting 23 | - **artlayer**: improve layer kind property to support artboard layer 24 | 25 | ## v0.22.10 (2024-12-29) 26 | 27 | ### Fix 28 | 29 | - **deps**: update dependency wheel to v0.45.1 30 | 31 | ## v0.22.9 (2024-11-27) 32 | 33 | ### Fix 34 | 35 | - **deps**: update dependency wheel to ^0.45.0 36 | 37 | ## v0.22.8 (2024-11-03) 38 | 39 | ### Fix 40 | 41 | - **deps**: update dependency comtypes to v1.4.8 42 | 43 | ## v0.22.7 (2024-11-01) 44 | 45 | ### Fix 46 | 47 | - **api/application**: Update return type hint for activeDocument method 48 | 49 | ## v0.22.6 (2024-11-01) 50 | 51 | ### Fix 52 | 53 | - fix publish to pypi 54 | 55 | ## v0.22.5 (2024-11-01) 56 | 57 | ### Fix 58 | 59 | - deploy version to 0.22.5 60 | - **version**: Add support for Photoshop 190 (2025) 61 | 62 | ## v0.22.4 (2023-12-04) 63 | 64 | ### Fix 65 | 66 | - **deps**: update dependency wheel to ^0.42.0 67 | 68 | ## v0.22.3 (2023-11-20) 69 | 70 | ### Fix 71 | 72 | - **text_item**: Remove unused import 73 | - **TextItem.font**: Change font to a read/write string property 74 | 75 | ## v0.22.2 (2023-11-08) 76 | 77 | ### Fix 78 | 79 | - **workflows**: Add quotes to prevent 3.10 being interpretted as 3.1 80 | - **typing**: Replace list with List as Python <= 3.8 does not support subscriptable types 81 | 82 | ### Refactor 83 | 84 | - **Photoshop**: Remove unnecessary exception type from suppress 85 | - **Application**: Fix incorrect docstring for app.purge, allow Path objects for app.load, wrap colorSettings exceptions 86 | - **Photoshop**: Restructure the sequence of events attempting to initialize a valid Dispatch object 87 | 88 | ## v0.22.1 (2023-09-22) 89 | 90 | ### Refactor 91 | 92 | - **TextFonts**: Add support for method 'get' and operator 'in' 93 | 94 | ## v0.22.0 (2023-09-18) 95 | 96 | ### Feat 97 | 98 | - Explicitly flag COM methods 99 | 100 | ### Fix 101 | 102 | - formatting and linting 103 | - comtypes does not play well with hasattr 104 | 105 | ## v0.21.10 (2023-09-14) 106 | 107 | ### Fix 108 | 109 | - fix auto bump version 110 | - fix can't set attribute `activeHistoryState` 111 | 112 | ## v0.21.9 (2023-08-31) 113 | 114 | ### Fix 115 | 116 | - **constants**: Update PHOTOSHOP_VERSION_MAPPINGS to add 2024 release mapping (v24/180.0) 117 | 118 | ## v0.21.8 (2023-08-22) 119 | 120 | ### Fix 121 | 122 | - **deps**: update dependency wheel to v0.41.2 123 | 124 | ## v0.21.7 (2023-08-17) 125 | 126 | ### Fix 127 | 128 | - **deps**: update dependency wheel to ^0.41.0 129 | 130 | ## v0.21.6 (2023-08-17) 131 | 132 | ### Fix 133 | 134 | - **test_imports.py**: Include root in install lines 135 | - **import-test.yml**: Try an alternate approach with multiple poetry install retries 136 | - **import-test.yml**: Correct typo in poetry version string 137 | - **import-test.yml**: Revert to <= poetry 1.5.0 to fix "Import Test" workflow 138 | - **import-test.yml**: Attempt to update lock to prevent inconsitent poetry.lock and pyproject.toml 139 | - **import-test**: Attempt to fix poetry bug in "Import Test" workflow 140 | - **deps**: Attempt to fix failing "Import Test" workflow 141 | 142 | ### Refactor 143 | 144 | - **TextFonts**: Implement __getitem__ to allow lookup by postScriptName 145 | 146 | ## v0.21.5 (2023-07-02) 147 | 148 | ### Fix 149 | 150 | - fix paste contents of the clipboard 151 | 152 | ## v0.21.4 (2023-06-13) 153 | 154 | ### Fix 155 | 156 | - **LayerSets**: Fix infinite recursion when trying to access LayerSets as a list 157 | 158 | ### Refactor 159 | 160 | - **Photoshop**: Attempt to get running Photoshop application before spawning new one 161 | 162 | ## v0.21.3 (2023-05-18) 163 | 164 | ### Fix 165 | 166 | - **deps**: update dependency wheel to ^0.40.0 167 | 168 | ## v0.21.2 (2023-05-17) 169 | 170 | ### Refactor 171 | 172 | - **artLayers,layerSets**: Refactored __getitem__ to work like a dictionary key on LayerSets. Added type hinting and try/except to __getitem__ for both ArtLayers and LayerSets 173 | 174 | ## v0.21.1 (2023-02-14) 175 | 176 | ### Refactor 177 | 178 | - update constants for support Photoshop 2023 179 | 180 | ## v0.21.0 (2023-01-06) 181 | 182 | ### Feat 183 | 184 | - **png.py**: Add optional args to PNGSaveOptions 185 | 186 | ## v0.20.1 (2022-11-28) 187 | 188 | ### Refactor 189 | 190 | - **action_descriptor**: Fix type hints for doubles 191 | 192 | ## v0.20.0 (2022-11-26) 193 | 194 | ### Feat 195 | 196 | - add a new option for create batch 197 | 198 | ## v0.19.7 (2022-11-14) 199 | 200 | ### Fix 201 | 202 | - **deps**: update dependency wheel to ^0.38.0 203 | 204 | ## v0.19.6 (2022-11-06) 205 | 206 | ### Refactor 207 | 208 | - update constants for support Photoshop 2022 209 | 210 | ## v0.19.5 (2022-07-17) 211 | 212 | ### Refactor 213 | 214 | - **application**: add a default value of action 215 | 216 | ## v0.19.4 (2022-07-10) 217 | 218 | ### Fix 219 | 220 | - get document by document name from documents. 221 | 222 | ## v0.19.3 (2022-06-17) 223 | 224 | ### Fix 225 | 226 | - fix import `EPSSaveOptions` 227 | 228 | ## v0.19.2 (2022-06-14) 229 | 230 | ### Refactor 231 | 232 | - **session.py**: add EPS save options 233 | 234 | ## v0.19.1 (2022-05-29) 235 | 236 | ### Fix 237 | 238 | - **document**: export document 239 | - **png**: exported PNG image is too large 240 | 241 | ## v0.19.0 (2022-05-20) 242 | 243 | ### Fix 244 | 245 | - **ArtLayer,-LayerSet**: adjusted linkedLayers property, fixed remove() 246 | 247 | ### Feat 248 | 249 | - **ArtLayer**: added linkedLayers and opacity, fixed unlink 250 | 251 | ## v0.18.1 (2022-04-17) 252 | 253 | ### Refactor 254 | 255 | - **ActionList**: Added ActionList to __init__ and Session to make ActionList callable from Application or Session object 256 | 257 | ## v0.18.0 (2022-04-04) 258 | 259 | ### Fix 260 | 261 | - fix export document 262 | 263 | ### Feat 264 | 265 | - add new function to convert as javascript 266 | 267 | ## v0.17.7 (2022-03-20) 268 | 269 | ### Fix 270 | 271 | - **deps**: update dependency wheel to ^0.37.0 272 | 273 | ## v0.17.6 (2022-03-19) 274 | 275 | ### Refactor 276 | 277 | - improve type hints 278 | 279 | ### Fix 280 | 281 | - add `ArtLayer` instance return when duplicate layer 282 | 283 | ## v0.17.5 (2022-03-13) 284 | 285 | ### Fix 286 | 287 | - update ci config and re-tag 288 | 289 | ## v0.17.4 (2022-03-13) 290 | 291 | ### Perf 292 | 293 | - add more docstrings 294 | 295 | ## v0.17.3 (2022-03-13) 296 | 297 | ### Perf 298 | 299 | - add more docstrings 300 | 301 | ## v0.17.2 (2022-03-13) 302 | 303 | ### Perf 304 | 305 | - improve docs 306 | 307 | ### Fix 308 | 309 | - retag and update ci 310 | 311 | ### Refactor 312 | 313 | - improve `getByName` from `artLayers` and `layers` 314 | 315 | ## v0.17.1 (2022-03-12) 316 | 317 | ### Refactor 318 | 319 | - improve docs 320 | 321 | ## v0.17.0 (2021-09-21) 322 | 323 | ### Feat 324 | 325 | - Update the logic of searching the installation path of Photoshop through the registration 326 | 327 | ## v0.16.3 (2021-09-12) 328 | 329 | ### Fix 330 | 331 | - add 2021 to version mappings 332 | 333 | ## v0.16.2 (2021-08-15) 334 | 335 | ### Fix 336 | 337 | - **api/text_item.py**: missing width.setter for paragraphtext in text_item.py 338 | 339 | ## v0.16.1 (2021-07-04) 340 | 341 | ### Fix 342 | 343 | - fix install failed in python-3.9 344 | 345 | ## v0.16.0 (2021-05-29) 346 | 347 | ### Feat 348 | 349 | - **documents**: support get document by index 350 | 351 | ## v0.15.2 (2021-05-29) 352 | 353 | ### Refactor 354 | 355 | - use absolute path imports 356 | - use absolute path imports 357 | 358 | ## 0.15.1 (2021-02-03) 359 | 360 | ## 0.15.0 (2021-01-10) 361 | 362 | ## 0.14.0 (2020-10-22) 363 | 364 | ## 0.13.0 (2020-09-23) 365 | 366 | ## 0.12.1 (2020-09-13) 367 | 368 | ## 0.12.0 (2020-05-10) 369 | 370 | ## 0.11.0 (2020-05-07) 371 | 372 | ## 0.10.0 (2020-04-22) 373 | 374 | ## 0.9.0 (2020-04-09) 375 | 376 | ## 0.8.0 (2020-04-08) 377 | 378 | ## 0.7.2 (2020-04-01) 379 | 380 | ## 0.3.0 (2020-02-24) 381 | 382 | ## 0.2.1 (2020-02-17) 383 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2019 Long Hao 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | {% 2 | include-markdown ".././CHANGELOG.md" 3 | %} 4 | -------------------------------------------------------------------------------- /docs/gen_api_nav.py: -------------------------------------------------------------------------------- 1 | """Plugin for generate API docs.""" 2 | 3 | # Import built-in modules 4 | from pathlib import Path 5 | 6 | # Import third-party modules 7 | import mkdocs_gen_files 8 | 9 | 10 | def main(): 11 | nav = mkdocs_gen_files.Nav() 12 | root = Path(__file__).parent.parent 13 | api_root = root.joinpath("photoshop") 14 | for path in sorted(Path(api_root).glob("**/*.py")): 15 | module_path = path.relative_to(root).with_suffix("") 16 | doc_path = path.relative_to(root).with_suffix(".md") 17 | full_doc_path = Path("reference", doc_path) 18 | parts = list(module_path.parts) 19 | if parts[-1] == "__init__": 20 | continue 21 | elif parts[-1] == "__main__": 22 | continue 23 | elif parts[-1] == "__version__": 24 | continue 25 | nav_parts = list(parts) 26 | if nav_parts[-1].startswith("_"): 27 | nav_parts[-1] = nav_parts[-1][1:] 28 | nav[nav_parts] = doc_path.as_posix().replace("\\", "/") 29 | full_doc_path = full_doc_path.as_posix().replace("\\", "/") 30 | with mkdocs_gen_files.open(full_doc_path, "w") as fd: 31 | ident = ".".join(parts) 32 | print(f"::: " + ident, file=fd) 33 | 34 | mkdocs_gen_files.set_edit_path(full_doc_path, path.as_posix().replace("\\", "/")) 35 | 36 | with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: 37 | nav_file.writelines(nav.build_literate_nav()) 38 | 39 | 40 | if __name__ == "": 41 | main() 42 | -------------------------------------------------------------------------------- /docs/gen_examples.py: -------------------------------------------------------------------------------- 1 | """Plugin for generate API docs.""" 2 | 3 | # Import built-in modules 4 | import os 5 | from pathlib import Path 6 | 7 | # Import third-party modules 8 | from jinja2 import Template 9 | import mkdocs_gen_files 10 | import stringcase 11 | 12 | 13 | template = Template( 14 | r""" 15 | Examples 16 | ======== 17 | {% for file_ in Examples.get_examples() %} 18 | {{ Examples.get_name(file_) }} 19 | {{ Examples.get_line(Examples.get_name(file_))}} 20 | ```python 21 | {{ Examples.get_content(file_) }} 22 | ``` 23 | {% endfor %} 24 | 25 | """ 26 | ) 27 | 28 | 29 | class Examples(object): 30 | def __init__(self, root: Path): 31 | self._root = root 32 | 33 | def get_examples(self): 34 | files = [file_ for file_ in self._root.glob("*.py") if "_psd_files.py" not in file_.as_posix()] 35 | return files 36 | 37 | @staticmethod 38 | def convert_relative_path(file): 39 | path = file.split("examples")[1] 40 | return "../examples{}".format(path.replace("\\", "/")) 41 | 42 | @staticmethod 43 | def get_name(file): 44 | name = os.path.basename(file).split(".py")[0] 45 | return stringcase.titlecase(name) 46 | 47 | @staticmethod 48 | def get_line(name): 49 | return "-" * len(name) 50 | 51 | @staticmethod 52 | def get_content(file_): 53 | with open(file_, "r") as f: 54 | return "".join(f.readlines()) 55 | 56 | 57 | def main(): 58 | root = Path(__file__).parent.parent 59 | with mkdocs_gen_files.open("examples.md", "w") as nav_file: 60 | examples_data = Examples(root.joinpath("examples")) 61 | nav_file.write(template.render(Examples=examples_data)) 62 | 63 | 64 | if __name__ == "": 65 | main() 66 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | {% 2 | include-markdown ".././README.md" 3 | %} -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | stringcase 2 | mkdocs-material = "^8.2.5" 3 | mkdocstrings-python = "^0.6.6" 4 | mkdocs-pymdownx-material-extras = "^1.6" 5 | mkdocs-same-dir = "^0.1.1" 6 | mkdocs-include-markdown-plugin = "^3.3.0" 7 | mkdocs-gen-files = "^0.3.4" 8 | mkdocs-autolinks-plugin = "^0.4.0" 9 | mkdocs-minify-plugin = "^0.5.0" 10 | mkdocs-git-revision-date-localized-plugin = "^1.0.0" 11 | mkdocs-literate-nav 12 | -------------------------------------------------------------------------------- /examples/_psd_files.py: -------------------------------------------------------------------------------- 1 | # Import built-in modules 2 | import os 3 | 4 | 5 | def get_psd_files(): 6 | files = {} 7 | this_root = os.path.dirname(__file__) 8 | file_root = os.path.join(this_root, "files") 9 | for file_name in os.listdir(file_root): 10 | files[file_name] = os.path.join(file_root, file_name) 11 | return files 12 | -------------------------------------------------------------------------------- /examples/active_layer.py: -------------------------------------------------------------------------------- 1 | """Example of working with active layers in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Get and set the active layer in a document 5 | 2. Create new art layers 6 | 3. Manage layer names and properties 7 | 8 | The script will: 9 | - Create a new document if none exists 10 | - Add a new art layer if document has less than 2 layers 11 | - Display the current active layer name 12 | - Create a new layer and rename it 13 | """ 14 | 15 | # Import local modules 16 | from photoshop import Session 17 | 18 | 19 | with Session() as ps: 20 | # Get or create a document 21 | if len(ps.app.documents) < 1: 22 | docRef = ps.app.documents.add() 23 | else: 24 | docRef = ps.app.activeDocument 25 | 26 | # Ensure we have at least 2 layers 27 | if len(docRef.layers) < 2: 28 | docRef.artLayers.add() 29 | 30 | # Display current active layer name 31 | ps.echo(docRef.activeLayer.name) 32 | 33 | # Create and rename a new layer 34 | new_layer = docRef.artLayers.add() 35 | ps.echo(new_layer.name) 36 | new_layer.name = "test" 37 | -------------------------------------------------------------------------------- /examples/add_metadata.py: -------------------------------------------------------------------------------- 1 | """Add metadata to current active document.""" 2 | 3 | # Import built-in modules 4 | import os 5 | 6 | # Import local modules 7 | from photoshop import Session 8 | 9 | 10 | with Session(action="new_document") as ps: 11 | doc = ps.active_document 12 | doc.info.author = os.getenv("USERNAME") 13 | doc.info.provinceState = "Beijing" 14 | doc.info.title = "My Demo" 15 | print("Print all metadata of current active document.") 16 | ps.echo(doc.info) 17 | -------------------------------------------------------------------------------- /examples/add_slate.py: -------------------------------------------------------------------------------- 1 | """Add slate information dynamically. 2 | 3 | - Open template. 4 | - Update info. 5 | - Save as jpg. 6 | - Close current document. 7 | 8 | """ 9 | 10 | # Import built-in modules 11 | from datetime import datetime 12 | import os 13 | from tempfile import mkdtemp 14 | 15 | # Import third-party modules 16 | import examples._psd_files as psd # Import from examples. 17 | 18 | # Import local modules 19 | from photoshop import Session 20 | 21 | 22 | PSD_FILE = psd.get_psd_files() 23 | slate_template = PSD_FILE["slate_template.psd"] 24 | with Session(slate_template, action="open", auto_close=True) as ps: 25 | layer_set = ps.active_document.layerSets.getByName("template") 26 | 27 | data = { 28 | "project name": "test_project", 29 | "datetime": datetime.today().strftime("%Y-%m-%d"), 30 | } 31 | for layer in layer_set.layers: 32 | if layer.kind == ps.LayerKind.TextLayer: 33 | layer.textItem.contents = data[layer.textItem.contents.strip()] 34 | 35 | jpg_file = os.path.join(mkdtemp("photoshop-python-api"), "slate.jpg") 36 | ps.active_document.saveAs(jpg_file, ps.JPEGSaveOptions()) 37 | print(f"Save jpg to {jpg_file}") 38 | os.startfile(jpg_file) 39 | -------------------------------------------------------------------------------- /examples/add_start_application_event.py: -------------------------------------------------------------------------------- 1 | """Add event for Photoshop start application. 2 | 3 | In the current example, every time we start photoshop it will 4 | alert "Start Application Event". 5 | 6 | Just like you manually in Script> Script Events Manager to enable the event. 7 | 8 | """ 9 | 10 | # Import built-in modules 11 | import os 12 | from tempfile import mkdtemp 13 | 14 | # Import local modules 15 | from photoshop import Session 16 | 17 | 18 | with Session() as ps: 19 | root = mkdtemp() 20 | jsx_file = os.path.join(root, "event.jsx") 21 | with open(jsx_file, "w") as f: 22 | f.write('alert("Start Application event.")') 23 | ps.app.notifiers.add(ps.EventID.Notify, jsx_file) 24 | print("Add event done.") 25 | -------------------------------------------------------------------------------- /examples/apply_crystallize_filter_action.py: -------------------------------------------------------------------------------- 1 | """ This script demonstrates how you can use the action manager 2 | 3 | to execute the Crystallize filter. 4 | In order to find all the IDs, see https://helpx.adobe.com/photoshop/kb/downloadable-plugins-and-content.html#ScriptingListenerplugin # noqa: E501 5 | This blog here explains what a script listener is http://blogs.adobe.com/crawlspace/2006/05/installing_and_1.html 6 | 7 | References: 8 | https://github.com/lohriialo/photoshop-scripting-python/blob/master/ApplyCrystallizeFilterAction.py 9 | 10 | """ 11 | 12 | # Import third-party modules 13 | import examples._psd_files as psd # Import from examples. 14 | 15 | # Import local modules 16 | from photoshop import Session 17 | 18 | 19 | PSD_FILE = psd.get_psd_files() 20 | 21 | with Session(PSD_FILE["layer_comps.psd"], "open") as ps: 22 | active_document = ps.active_document 23 | nLayerSets = active_document.layerSets 24 | print(f"The total amount of current layerSet (Group) is " f"{len(nLayerSets)}.") 25 | nArtLayers = active_document.layerSets.item(len(nLayerSets)).artLayers 26 | 27 | # get the last layer in LayerSets 28 | active_document.activeLayer = active_document.layerSets.item(len(nLayerSets)).artLayers.item(len(nArtLayers)) 29 | 30 | def applyCrystallize(cellSize): 31 | cellSizeID = ps.app.CharIDToTypeID("ClSz") 32 | eventCrystallizeID = ps.app.CharIDToTypeID("Crst") 33 | 34 | filterDescriptor = ps.ActionDescriptor 35 | filterDescriptor.putInteger(cellSizeID, cellSize) 36 | 37 | ps.app.executeAction(eventCrystallizeID, filterDescriptor) 38 | 39 | applyCrystallize(25) 40 | print("Apply crystallize done.") 41 | -------------------------------------------------------------------------------- /examples/apply_filters.py: -------------------------------------------------------------------------------- 1 | """Example of applying various filters to layers in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Apply different types of Photoshop filters 5 | 2. Configure filter parameters 6 | 3. Work with filter options 7 | 4. Handle filter application to different layer types 8 | 9 | Key concepts: 10 | - Filter types and options 11 | - Layer filtering 12 | - Parameter configuration 13 | - Filter effects management 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | layer = doc.activeLayer 23 | 24 | # Apply Gaussian Blur filter 25 | filter_options = ps.GaussianBlurOptions() 26 | filter_options.radius = 10.0 27 | layer.applyGaussianBlur(filter_options) 28 | 29 | # Create new layer for other filters 30 | new_layer = doc.artLayers.add() 31 | 32 | # Apply Motion Blur 33 | motion_options = ps.MotionBlurOptions() 34 | motion_options.angle = 45 35 | motion_options.distance = 20 36 | new_layer.applyMotionBlur(motion_options) 37 | 38 | # Apply Smart Sharpen 39 | sharpen_options = ps.SmartSharpenOptions() 40 | sharpen_options.amount = 100 41 | sharpen_options.radius = 3.0 42 | sharpen_options.noiseReduction = 20 43 | new_layer.applySmartSharpen(sharpen_options) 44 | 45 | # Apply Unsharp Mask 46 | unsharp_options = ps.UnsharpMaskOptions() 47 | unsharp_options.amount = 50 48 | unsharp_options.radius = 2.0 49 | unsharp_options.threshold = 0 50 | new_layer.applyUnsharpMask(unsharp_options) 51 | -------------------------------------------------------------------------------- /examples/change_color_of_background_and_foreground.py: -------------------------------------------------------------------------------- 1 | """Example of changing background and foreground colors in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Set foreground and background colors 5 | 2. Work with color swatches 6 | 3. Switch between foreground and background colors 7 | 4. Reset colors to default values 8 | 9 | Key concepts: 10 | - Color management 11 | - Foreground/background colors 12 | - Color swatches 13 | - Default colors 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | # Create new colors 22 | fg_color = ps.SolidColor() 23 | fg_color.rgb.red = 255 24 | fg_color.rgb.green = 0 25 | fg_color.rgb.blue = 0 26 | 27 | bg_color = ps.SolidColor() 28 | bg_color.rgb.red = 0 29 | bg_color.rgb.green = 0 30 | bg_color.rgb.blue = 255 31 | 32 | # Set foreground and background colors 33 | ps.app.foregroundColor = fg_color 34 | ps.app.backgroundColor = bg_color 35 | 36 | # Print current colors 37 | ps.echo(f"Foreground RGB: {ps.app.foregroundColor.rgb.red}, " 38 | f"{ps.app.foregroundColor.rgb.green}, " 39 | f"{ps.app.foregroundColor.rgb.blue}") 40 | 41 | ps.echo(f"Background RGB: {ps.app.backgroundColor.rgb.red}, " 42 | f"{ps.app.backgroundColor.rgb.green}, " 43 | f"{ps.app.backgroundColor.rgb.blue}") 44 | -------------------------------------------------------------------------------- /examples/color.py: -------------------------------------------------------------------------------- 1 | """Example of working with colors in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create and modify solid colors 5 | 2. Work with different color models (RGB, CMYK, HSB) 6 | 3. Set foreground and background colors 7 | 4. Compare color values 8 | 9 | Key concepts: 10 | - Color models 11 | - Color manipulation 12 | - Color space conversion 13 | - Color comparison 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | # Create a new RGB color 22 | rgb_color = ps.SolidColor() 23 | rgb_color.rgb.red = 255 24 | rgb_color.rgb.green = 0 25 | rgb_color.rgb.blue = 0 26 | 27 | # Create a new CMYK color 28 | cmyk_color = ps.SolidColor() 29 | cmyk_color.cmyk.cyan = 0 30 | cmyk_color.cmyk.magenta = 100 31 | cmyk_color.cmyk.yellow = 100 32 | cmyk_color.cmyk.black = 0 33 | 34 | # Set as foreground color 35 | ps.app.foregroundColor = rgb_color 36 | 37 | # Create HSB color 38 | hsb_color = ps.SolidColor() 39 | hsb_color.hsb.hue = 360 40 | hsb_color.hsb.saturation = 100 41 | hsb_color.hsb.brightness = 100 42 | 43 | # Set as background color 44 | ps.app.backgroundColor = hsb_color 45 | -------------------------------------------------------------------------------- /examples/compare_colors.py: -------------------------------------------------------------------------------- 1 | """Example of comparing colors in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Compare colors across different color models 5 | 2. Convert between color spaces 6 | 3. Check color equality 7 | 4. Work with color tolerances 8 | 9 | Key concepts: 10 | - Color comparison 11 | - Color space conversion 12 | - Color equality testing 13 | - Color model differences 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | # Create two colors for comparison 22 | color1 = ps.SolidColor() 23 | color1.rgb.red = 255 24 | color1.rgb.green = 0 25 | color1.rgb.blue = 0 26 | 27 | color2 = ps.SolidColor() 28 | color2.rgb.red = 255 29 | color2.rgb.green = 0 30 | color2.rgb.blue = 0 31 | 32 | # Compare colors 33 | is_same = (color1.rgb.red == color2.rgb.red and 34 | color1.rgb.green == color2.rgb.green and 35 | color1.rgb.blue == color2.rgb.blue) 36 | 37 | ps.echo(f"Colors are {'same' if is_same else 'different'}") 38 | -------------------------------------------------------------------------------- /examples/convert_smartobject_to_layer.py: -------------------------------------------------------------------------------- 1 | """Example of converting between smart objects and regular layers. 2 | 3 | This example demonstrates how to: 4 | 1. Convert layers to smart objects 5 | 2. Convert smart objects back to regular layers 6 | 3. Manage smart object properties 7 | 4. Handle smart object conversions 8 | 9 | Key concepts: 10 | - Smart Objects 11 | - Layer conversion 12 | - Smart Object properties 13 | - Layer types 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create a new layer 24 | layer = doc.artLayers.add() 25 | layer.name = "Test Layer" 26 | 27 | # Convert to smart object 28 | layer.convertToSmartObject() 29 | ps.echo("Layer converted to Smart Object") 30 | 31 | # Check if it's a smart object 32 | if layer.kind == ps.LayerKind.SmartObjectLayer: 33 | ps.echo("Layer is now a Smart Object") 34 | 35 | # Convert back to regular layer 36 | layer.rasterize(ps.RasterizeType.EntireLayer) 37 | ps.echo("Smart Object converted back to regular layer") 38 | -------------------------------------------------------------------------------- /examples/copy_and_paste.py: -------------------------------------------------------------------------------- 1 | """ 2 | References: 3 | https://github.com/lohriialo/photoshop-scripting-python/blob/master/CopyAndPaste.py 4 | 5 | """ 6 | 7 | # Import local modules 8 | import photoshop.api as ps 9 | 10 | 11 | app = ps.Application() 12 | 13 | startRulerUnits = app.preferences.rulerUnits 14 | 15 | app.preferences.rulerUnits = ps.Units.Inches 16 | 17 | doc = app.documents.add(7, 5, 72, None, ps.NewDocumentMode.NewRGB, ps.DocumentFill.White) 18 | 19 | # Make sure the active layer is not a text layer, which cannot be copied to the 20 | # clipboard. 21 | if doc.activeLayer.kind != ps.LayerKind.TextLayer: 22 | # Select the left half of the document. Selections are always expressed 23 | # in pixels regardless of the current ruler unit type, so we're computing 24 | # the selection corner points based on the inch unit width and height 25 | # of the document 26 | x2 = (doc.width * doc.resolution) / 2 27 | y2 = doc.height * doc.resolution 28 | 29 | sel_area = ((0, 0), (x2, 0), (x2, y2), (0, y2)) 30 | doc.selection.select(sel_area, ps.SelectionType.ReplaceSelection, 0, False) 31 | 32 | doc.selection.copy() 33 | 34 | # The new doc is created 35 | # need to change ruler units to pixels because x2 and y2 are pixel units. 36 | app.preferences.rulerUnits = ps.Units.Pixels 37 | pasteDoc = app.documents.add(x2, y2, doc.resolution, "Paste Target") 38 | pasteDoc.paste() 39 | else: 40 | print("You cannot copy from a text layer") 41 | 42 | if startRulerUnits != app.preferences.rulerUnits: 43 | app.preferences.rulerUnits = startRulerUnits 44 | -------------------------------------------------------------------------------- /examples/create_new_document.py: -------------------------------------------------------------------------------- 1 | """Example of creating a new document in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create a new document with specific dimensions 5 | 2. Set document properties like name and mode 6 | 3. Work with document units and resolution 7 | 8 | Key concepts: 9 | - Document creation 10 | - Document properties 11 | - Color mode settings 12 | """ 13 | 14 | # Import local modules 15 | from photoshop import Session 16 | 17 | 18 | with Session() as ps: 19 | # Create a new document with specific dimensions 20 | doc = ps.app.documents.add( 21 | width=1920, 22 | height=1080, 23 | resolution=72, 24 | name="New Document Example" 25 | ) 26 | -------------------------------------------------------------------------------- /examples/create_thumbnail.py: -------------------------------------------------------------------------------- 1 | """Create a thumbnail image for currently active document. 2 | 3 | You can use the thumbnail image to upload to Shotgun or Ftrack. 4 | 5 | """ 6 | 7 | # Import built-in modules 8 | import os 9 | from tempfile import mkdtemp 10 | 11 | # Import local modules 12 | from photoshop import Session 13 | 14 | 15 | def create_thumbnail(output_path=None, max_resolution=512): 16 | """Create a thumbnail image for currently active document. 17 | 18 | Args: 19 | output_path (str): The absolute output path of the thumbnail image. 20 | The default is to output to a temporary folder. 21 | max_resolution (int): The max resolution of the thumbnail. The default 22 | is `512`. 23 | 24 | Returns: 25 | str: The absolute output path of the thumbnail image. 26 | 27 | """ 28 | output_path = output_path or os.path.join(mkdtemp(), "thumb.jpg") 29 | 30 | with Session(auto_close=True) as ps: 31 | orig_name = ps.active_document.name 32 | width_str = ps.active_document.width 33 | height_str = ps.active_document.height 34 | thumb_name = f"{orig_name}_thumb" 35 | 36 | max_resolution = width_str / max_resolution 37 | thumb_width = int(width_str / max_resolution) 38 | thumb_height = int(height_str / max_resolution) 39 | 40 | thumb_doc = ps.active_document.duplicate(thumb_name) 41 | thumb_doc.resizeImage(thumb_width, thumb_height - 100) 42 | thumb_doc.saveAs(output_path, ps.JPEGSaveOptions(), asCopy=True) 43 | thumb_doc.close() 44 | return output_path 45 | 46 | 47 | if __name__ == "__main__": 48 | thumb_file = create_thumbnail() 49 | print(f"Save thumbnail file to {thumb_file}.") 50 | -------------------------------------------------------------------------------- /examples/creating_a_layer.py: -------------------------------------------------------------------------------- 1 | """Example of creating and manipulating layers in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create different types of layers 5 | 2. Set layer properties and attributes 6 | 3. Organize layers in the document 7 | 4. Apply basic layer effects 8 | 9 | Key concepts: 10 | - Layer creation 11 | - Layer types (art layers, text layers) 12 | - Layer properties 13 | - Layer organization 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create a new art layer 24 | new_layer = doc.artLayers.add() 25 | 26 | # Set layer properties 27 | new_layer.name = "New Art Layer" 28 | new_layer.opacity = 75 29 | new_layer.visible = True 30 | 31 | # Create a text layer 32 | text_layer = doc.artLayers.add() 33 | text_layer.kind = ps.LayerKind.TextLayer 34 | 35 | # Configure text properties 36 | text_item = text_layer.textItem 37 | text_item.contents = "Sample Text" 38 | text_item.size = 72 39 | text_item.position = [100, 100] 40 | 41 | # Move layers in stack 42 | new_layer.move(text_layer, ps.ElementPlacement.PlaceAfter) 43 | -------------------------------------------------------------------------------- /examples/cropping.py: -------------------------------------------------------------------------------- 1 | """A cropping example.""" 2 | 3 | # Import local modules 4 | from photoshop import Session 5 | 6 | 7 | with Session(action="new_document") as ps: 8 | ps.active_document.crop(bounds=[100, 12, 354, 246], width=1920, height=1080) 9 | -------------------------------------------------------------------------------- /examples/current_tool.py: -------------------------------------------------------------------------------- 1 | """Example of working with Photoshop tools. 2 | 3 | This example demonstrates how to: 4 | 1. Get the current active tool 5 | 2. Change tools programmatically 6 | 3. Configure tool options 7 | 4. Work with tool presets 8 | 9 | Key concepts: 10 | - Tool selection 11 | - Tool properties 12 | - Tool management 13 | - Active tool state 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | # Get current tool 22 | current = ps.app.currentTool 23 | 24 | # Print current tool name 25 | ps.echo(f"Current tool: {current}") 26 | -------------------------------------------------------------------------------- /examples/delete_and_fill_selection.py: -------------------------------------------------------------------------------- 1 | """This script demonstrates how to delete and fill a selection in one operation.""" 2 | 3 | # Import built-in modules 4 | import os 5 | 6 | # Import third-party modules 7 | from photoshop import Session 8 | from photoshop.api import SolidColor 9 | import photoshop.api as ps 10 | 11 | # Import local modules 12 | from _psd_files import get_psd_files 13 | 14 | def delete_and_fill_selection(doc, fill_type, mode=None, opacity=None, preserve_transparency=None): 15 | """Delete current selection and fill it with specified color. 16 | 17 | Args: 18 | doc: The active document. 19 | fill_type (SolidColor): The color to fill the selection with. 20 | mode (ColorBlendMode, optional): The color blend mode. 21 | opacity (int, optional): The opacity value. 22 | preserve_transparency (bool, optional): If true, preserves transparency. 23 | """ 24 | # First fill the selection 25 | doc.selection.fill(fill_type, mode, opacity, preserve_transparency) 26 | # Then deselect 27 | doc.selection.deselect() 28 | 29 | def main(): 30 | """Create a selection and fill it with a solid color.""" 31 | psd_file = get_psd_files()["export_layers_as_png.psd"] 32 | 33 | # Initialize Photoshop application 34 | app = ps.Application() 35 | 36 | # Open the test file 37 | if not os.path.exists(psd_file): 38 | raise FileNotFoundError(f"Test file not found: {psd_file}") 39 | app.load(psd_file) 40 | 41 | # Get the active document 42 | doc = app.activeDocument 43 | 44 | # Create a rectangular selection 45 | doc.selection.select(((100, 100), (400, 100), (400, 300), (100, 300))) 46 | 47 | # Create a solid color (red in this case) 48 | red_color = SolidColor() 49 | red_color.rgb.red = 255 50 | red_color.rgb.green = 0 51 | red_color.rgb.blue = 0 52 | 53 | # Delete and fill the selection 54 | delete_and_fill_selection(doc, red_color, opacity=80) 55 | 56 | # Save the changes 57 | doc.save() 58 | 59 | print("Selection has been filled with red color.") 60 | 61 | if __name__ == "__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /examples/do_photoshop_action.py: -------------------------------------------------------------------------------- 1 | """Example of executing Photoshop actions. 2 | 3 | This example demonstrates how to: 4 | 1. Play recorded Photoshop actions 5 | 2. Work with action sets 6 | 3. Handle action execution 7 | 4. Manage action parameters 8 | 9 | Key concepts: 10 | - Action playback 11 | - Action sets 12 | - Action execution 13 | - Automation 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | # Play default Photoshop action 22 | ps.app.doAction("action_name", "set_name") 23 | -------------------------------------------------------------------------------- /examples/emboss_action.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop import Session 3 | 4 | 5 | with Session() as ps: 6 | app = ps.app 7 | for index, x in enumerate(range(50)): 8 | # Execute an existing action from action palette. 9 | idPly = app.charIDToTypeID("Ply ") 10 | desc8 = ps.ActionDescriptor() 11 | idnull = app.charIDToTypeID("null") 12 | ref3 = ps.ActionReference() 13 | idActn = app.charIDToTypeID("Actn") 14 | ref3.putName(idActn, "Sepia Toning (layer)") 15 | idASet = app.charIDToTypeID("ASet") 16 | ref3.PutName(idASet, "Default Actions") 17 | desc8.putReference(idnull, ref3) 18 | app.executeAction(idPly, desc8, ps.DialogModes.DisplayNoDialogs) 19 | 20 | # Create solid color fill layer. 21 | idMk = app.charIDToTypeID("Mk ") 22 | desc21 = ps.ActionDescriptor() 23 | idNull = app.charIDToTypeID("null") 24 | ref12 = ps.ActionReference() 25 | idContentLayer1 = app.stringIDToTypeID("contentLayer") 26 | ref12.putClass(idContentLayer1) 27 | desc21.putReference(idNull, ref12) 28 | idUsng = app.charIDToTypeID("Usng") 29 | desc22 = ps.ActionDescriptor() 30 | idType = app.charIDToTypeID("Type") 31 | desc23 = ps.ActionDescriptor() 32 | idClr = app.charIDToTypeID("Clr ") 33 | desc24 = ps.ActionDescriptor() 34 | idRd = app.charIDToTypeID("Rd ") 35 | desc24.putDouble(idRd, index) 36 | idGrn = app.charIDToTypeID("Grn ") 37 | desc24.putDouble(idGrn, index) 38 | idBl = app.charIDToTypeID("Bl ") 39 | desc24.putDouble(idBl, index) 40 | idRGBC = app.charIDToTypeID("RGBC") 41 | desc23.putObject(idClr, idRGBC, desc24) 42 | idSolidColorLayer = app.StringIDToTypeID("solidColorLayer") 43 | desc22.putObject(idType, idSolidColorLayer, desc23) 44 | idContentLayer2 = app.StringIDToTypeID("contentLayer") 45 | desc21.putObject(idUsng, idContentLayer2, desc22) 46 | app.executeAction(idMk, desc21, ps.DialogModes.DisplayNoDialogs) 47 | 48 | # Select mask. 49 | idSlct = app.charIDToTypeID("slct") 50 | desc38 = ps.ActionDescriptor() 51 | idNull1 = app.charIDToTypeID("null") 52 | ref20 = ps.ActionReference() 53 | idChnl1 = app.charIDToTypeID("Chnl") 54 | idChnl2 = app.charIDToTypeID("Chnl") 55 | idMsk = app.charIDToTypeID("Msk ") 56 | ref20.putEnumerated(idChnl1, idChnl2, idMsk) 57 | desc38.putReference(idNull1, ref20) 58 | idMkVs = app.charIDToTypeID("MkVs") 59 | desc38.putBoolean(idMkVs, False) 60 | app.executeAction(idSlct, desc38, ps.DialogModes.DisplayNoDialogs) 61 | 62 | app.activeDocument.activeLayer.invert() 63 | -------------------------------------------------------------------------------- /examples/enable_generator.py: -------------------------------------------------------------------------------- 1 | """Enable Generator features.""" 2 | # Import local modules 3 | from photoshop import Session 4 | 5 | 6 | with Session() as ps: 7 | plugin_name = "generator-assets-dummy-menu" 8 | generatorDesc = ps.ActionDescriptor 9 | generatorDesc.putString(ps.app.stringIDToTypeID("name"), plugin_name) 10 | ps.app.executeAction(ps.app.stringIDToTypeID("generateAssets"), generatorDesc) 11 | -------------------------------------------------------------------------------- /examples/eval_javascript.py: -------------------------------------------------------------------------------- 1 | """Example of executing JavaScript code in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Execute JavaScript commands 5 | 2. Interact with Photoshop's scripting engine 6 | 3. Handle JavaScript results 7 | 4. Pass data between Python and JavaScript 8 | 9 | Key concepts: 10 | - JavaScript execution 11 | - Script integration 12 | - Data exchange 13 | - Command execution 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | # Execute JavaScript command 22 | js_code = "app.documents.length" 23 | result = ps.app.eval_javascript(js_code) 24 | ps.echo(f"Number of open documents: {result}") 25 | -------------------------------------------------------------------------------- /examples/export_document.py: -------------------------------------------------------------------------------- 1 | """Example of exporting a Photoshop document to different formats. 2 | 3 | This example demonstrates how to: 4 | 1. Save documents in different formats (JPG, PNG) 5 | 2. Configure export quality settings 6 | 3. Handle file paths and naming 7 | 4. Use save options for different formats 8 | 9 | Key concepts: 10 | - File format conversion 11 | - Export quality control 12 | - Save options configuration 13 | - File path handling 14 | """ 15 | 16 | # Import built-in modules 17 | import os 18 | 19 | # Import local modules 20 | from photoshop import Session 21 | 22 | 23 | with Session() as ps: 24 | doc = ps.active_document 25 | 26 | # Get the directory of current script 27 | current_dir = os.path.dirname(__file__) 28 | 29 | # Save as JPG with high quality 30 | jpg_opt = ps.JPEGSaveOptions(quality=12) 31 | jpg_path = os.path.join(current_dir, "output.jpg") 32 | doc.saveAs(jpg_path, jpg_opt) 33 | 34 | # Save as PNG with transparency 35 | png_opt = ps.PhotoshopSaveOptions() 36 | png_path = os.path.join(current_dir, "output.png") 37 | doc.saveAs(png_path, png_opt) 38 | -------------------------------------------------------------------------------- /examples/export_document_with_options.py: -------------------------------------------------------------------------------- 1 | """This example demonstrates how to export a document with different formats. 2 | 3 | References: 4 | https://github.com/loonghao/photoshop-python-api/issues/368 5 | """ 6 | 7 | # Import built-in modules 8 | import os 9 | 10 | # Import third-party modules 11 | from photoshop import Session 12 | from photoshop.api.enumerations import DitherType 13 | from photoshop.api.enumerations import ExportType 14 | from photoshop.api.enumerations import SaveDocumentType 15 | from photoshop.api.save_options.png import PNGSaveOptions 16 | from photoshop.api.save_options.jpg import JPEGSaveOptions 17 | from photoshop.api.save_options.png import ExportOptionsSaveForWeb 18 | 19 | 20 | def main(): 21 | """Export document with different formats.""" 22 | psd_file = os.path.join(os.path.dirname(__file__), "files", "export_layers_as_png.psd") 23 | if not os.path.exists(psd_file): 24 | raise FileNotFoundError( 25 | f"File not found: {psd_file}" 26 | ) 27 | 28 | # Start a new photoshop session 29 | with Session(psd_file, "open") as ps: 30 | doc = ps.active_document 31 | 32 | # Export as PNG-24 33 | png_path = os.path.join(os.path.dirname(__file__), "exported_png24.png") 34 | png_options = PNGSaveOptions() 35 | png_options.interlaced = False # Disable interlacing for better quality 36 | png_options.compression = 0 # Set compression to 0 for maximum quality 37 | doc.saveAs(png_path, png_options, True) # True for saving as copy 38 | print(f"Exported PNG-24: {png_path}") 39 | 40 | # Export as JPEG with high quality 41 | jpg_path = os.path.join(os.path.dirname(__file__), "exported_jpeg.jpg") 42 | jpg_options = JPEGSaveOptions() 43 | jpg_options.quality = 12 # Set quality to maximum (12) 44 | jpg_options.embedColorProfile = True # Preserve color profile 45 | jpg_options.formatOptions = 1 # Use standard baseline format 46 | jpg_options.scans = 3 # Enable progressive scanning 47 | jpg_options.matte = 1 # No background color (matte) 48 | doc.saveAs(jpg_path, jpg_options, True) # True for saving as copy 49 | print(f"Exported JPEG: {jpg_path}") 50 | 51 | # Export as GIF using Save for Web 52 | gif_path = os.path.join(os.path.dirname(__file__), "exported_gif.gif") 53 | gif_options = ExportOptionsSaveForWeb() 54 | gif_options.format = SaveDocumentType.CompuServeGIFSave # Set format to GIF 55 | gif_options.colors = 256 # Use maximum number of colors (256) 56 | gif_options.dither = DitherType.NoDither # Disable dithering for sharper edges 57 | gif_options.transparency = True # Preserve transparency in the GIF 58 | doc.exportDocument(gif_path, ExportType.SaveForWeb, gif_options) 59 | print(f"Exported GIF: {gif_path}") 60 | 61 | 62 | if __name__ == "__main__": 63 | main() 64 | -------------------------------------------------------------------------------- /examples/export_layers_as_png.py: -------------------------------------------------------------------------------- 1 | """Example of exporting individual layers as PNG files. 2 | 3 | This example demonstrates how to: 4 | 1. Export each layer of a document as a separate PNG file 5 | 2. Handle layer visibility during export 6 | 3. Manage layer selection and activation 7 | 4. Configure export settings for PNG format 8 | 9 | Key concepts: 10 | - Layer iteration 11 | - Visibility management 12 | - PNG export settings 13 | - File naming conventions 14 | - Layer selection 15 | """ 16 | 17 | # Import built-in modules 18 | import os 19 | 20 | # Import local modules 21 | from photoshop import Session 22 | 23 | 24 | with Session() as ps: 25 | doc = ps.active_document 26 | 27 | # Store original layer visibilities 28 | layer_visibilities = [] 29 | for layer in doc.layers: 30 | layer_visibilities.append(layer.visible) 31 | layer.visible = False 32 | 33 | try: 34 | # Export each layer individually 35 | for i, layer in enumerate(doc.layers): 36 | # Show only current layer 37 | layer.visible = True 38 | 39 | # Configure PNG save options 40 | options = ps.PNGSaveOptions() 41 | options.interlaced = False 42 | 43 | # Generate unique filename for each layer 44 | file_path = os.path.join( 45 | os.path.dirname(__file__), 46 | f"layer_{i}_{layer.name}.png" 47 | ) 48 | 49 | # Save the file 50 | doc.saveAs(file_path, options, True) 51 | 52 | # Hide the layer again 53 | layer.visible = False 54 | 55 | finally: 56 | # Restore original layer visibilities 57 | for layer, visibility in zip(doc.layers, layer_visibilities): 58 | layer.visible = visibility 59 | -------------------------------------------------------------------------------- /examples/export_layers_use_export_options_saveforweb.py: -------------------------------------------------------------------------------- 1 | """Export every layer as a .png file use `ExportOptionsSaveForWeb`.""" 2 | # Import built-in modules 3 | import os 4 | 5 | # Import third-party modules 6 | import examples._psd_files as psd # Import from examples. 7 | 8 | # Import local modules 9 | from photoshop import Session 10 | 11 | 12 | PSD_FILE = psd.get_psd_files() 13 | 14 | 15 | def hide_all_layers(layers): 16 | for layer in layers: 17 | layer.visible = False 18 | 19 | 20 | def main(): 21 | psd_file = PSD_FILE["export_layers_as_png.psd"] 22 | with Session(psd_file, action="open") as ps: 23 | doc = ps.active_document 24 | options = ps.ExportOptionsSaveForWeb() 25 | layers = doc.artLayers 26 | for layer in layers: 27 | hide_all_layers(layers) 28 | layer.visible = True 29 | layer_path = os.path.join(doc.path, layer.name) 30 | print(layer_path) 31 | if not os.path.exists(layer_path): 32 | os.makedirs(layer_path) 33 | image_path = os.path.join(layer_path, f"{layer.name}.png") 34 | doc.exportDocument(image_path, exportAs=ps.ExportType.SaveForWeb, options=options) 35 | ps.alert("Task done!") 36 | ps.echo(doc.activeLayer) 37 | 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /examples/files/artboard_example.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/artboard_example.psd -------------------------------------------------------------------------------- /examples/files/export_layers_as_png.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/export_layers_as_png.psd -------------------------------------------------------------------------------- /examples/files/green_130x260.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/green_130x260.png -------------------------------------------------------------------------------- /examples/files/layer_comps.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/layer_comps.psd -------------------------------------------------------------------------------- /examples/files/red_100x200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/red_100x200.png -------------------------------------------------------------------------------- /examples/files/replace_images.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/replace_images.psd -------------------------------------------------------------------------------- /examples/files/slate_template.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/slate_template.psd -------------------------------------------------------------------------------- /examples/files/trim.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/examples/files/trim.psd -------------------------------------------------------------------------------- /examples/fill_selection.py: -------------------------------------------------------------------------------- 1 | """Example of filling selections in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create and modify selections 5 | 2. Fill selections with colors 6 | 3. Work with selection options 7 | 4. Apply different fill methods 8 | 9 | Key concepts: 10 | - Selection tools 11 | - Fill operations 12 | - Color fills 13 | - Selection modification 14 | - Fill opacity and blending 15 | """ 16 | 17 | # Import local modules 18 | from photoshop import Session 19 | 20 | 21 | with Session() as ps: 22 | doc = ps.active_document 23 | 24 | # Create a rectangular selection 25 | doc.selection.select([ 26 | [100, 100], 27 | [300, 100], 28 | [300, 200], 29 | [100, 200] 30 | ]) 31 | 32 | # Create fill color 33 | fill_color = ps.SolidColor() 34 | fill_color.rgb.red = 255 35 | fill_color.rgb.green = 0 36 | fill_color.rgb.blue = 0 37 | 38 | # Fill the selection 39 | doc.selection.fill(fill_color) 40 | 41 | # Deselect 42 | doc.selection.deselect() 43 | 44 | # Create another selection and fill with opacity 45 | doc.selection.select([ 46 | [150, 150], 47 | [350, 150], 48 | [350, 250], 49 | [150, 250] 50 | ]) 51 | 52 | fill_color.rgb.blue = 255 53 | doc.selection.fill(fill_color, ps.ColorBlendMode.Normal, 50) 54 | 55 | # Clear selection 56 | doc.selection.deselect() 57 | -------------------------------------------------------------------------------- /examples/fit_on_screen.py: -------------------------------------------------------------------------------- 1 | """Let the current document Fit on screen.""" 2 | 3 | # Import local modules 4 | from photoshop import Session 5 | 6 | 7 | with Session() as ps: 8 | ps.app.runMenuItem(ps.app.charIDToTypeID("FtOn")) 9 | -------------------------------------------------------------------------------- /examples/get_document_by_name.py: -------------------------------------------------------------------------------- 1 | """Example of retrieving a Photoshop document by its name. 2 | 3 | This example demonstrates how to: 4 | 1. Access documents in the Photoshop application 5 | 2. Find a specific document by its name 6 | 3. Handle cases when the document doesn't exist 7 | 8 | Key concepts: 9 | - Using the documents collection 10 | - Document name comparison 11 | - Error handling for missing documents 12 | """ 13 | 14 | # Import local modules 15 | from photoshop import Session 16 | 17 | 18 | with Session() as ps: 19 | # Try to get document named 'test.psd' 20 | for doc in ps.app.documents: 21 | if doc.name == "test.psd": 22 | ps.echo(doc.name) 23 | break 24 | else: 25 | ps.echo("Document not found!") 26 | -------------------------------------------------------------------------------- /examples/get_layer_by_name.py: -------------------------------------------------------------------------------- 1 | """Example of retrieving a layer by its name in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Access layers in the active document 5 | 2. Find a specific layer by its name 6 | 3. Handle layer search in the document hierarchy 7 | 8 | Key concepts: 9 | - Layer navigation 10 | - Name-based layer lookup 11 | - Active document context 12 | """ 13 | 14 | # Import local modules 15 | from photoshop import Session 16 | 17 | 18 | with Session() as ps: 19 | # Access active document's layers 20 | doc = ps.app.activeDocument 21 | for layer in doc.layers: 22 | if layer.name == "example layer": 23 | ps.echo(layer.name) 24 | break 25 | -------------------------------------------------------------------------------- /examples/hello_world.py: -------------------------------------------------------------------------------- 1 | """Basic example demonstrating Photoshop automation with python-photoshop-api. 2 | 3 | This example demonstrates how to: 4 | 1. Connect to Photoshop application 5 | 2. Create a new document 6 | 3. Add text content to the document 7 | 4. Save the document as PSD file 8 | 9 | Key concepts: 10 | - Application connection 11 | - Document creation 12 | - Text layer manipulation 13 | - File saving 14 | """ 15 | 16 | # Import built-in modules 17 | import os 18 | 19 | # Import local modules 20 | from photoshop import Session 21 | 22 | 23 | with Session() as ps: 24 | # Create a new document 25 | doc = ps.app.documents.add() 26 | 27 | # Create text layer with "Hello, World!" 28 | text_color = ps.SolidColor() 29 | text_color.rgb.red = 255 30 | text_color.rgb.green = 0 31 | text_color.rgb.blue = 0 32 | 33 | new_text_layer = doc.artLayers.add() 34 | new_text_layer.kind = ps.LayerKind.TextLayer 35 | new_text_layer.textItem.contents = "Hello, World!" 36 | new_text_layer.textItem.position = [160, 167] 37 | new_text_layer.textItem.size = 40 38 | new_text_layer.textItem.color = text_color 39 | 40 | # Save the document 41 | jpg_file = os.path.join(os.path.dirname(__file__), "hello_world.jpg") 42 | ps.JPEGSaveOptions(quality=12) 43 | doc.saveAs(jpg_file) 44 | -------------------------------------------------------------------------------- /examples/import_image_as_layer.py: -------------------------------------------------------------------------------- 1 | """Import a image as a artLayer.""" 2 | 3 | # Import local modules 4 | from photoshop import Session 5 | 6 | 7 | with Session(action="new_document") as ps: 8 | desc = ps.ActionDescriptor 9 | desc.putPath(ps.app.charIDToTypeID("null"), "your/image/path") 10 | event_id = ps.app.charIDToTypeID("Plc ") # `Plc` need one space in here. 11 | ps.app.executeAction(ps.app.charIDToTypeID("Plc "), desc) 12 | -------------------------------------------------------------------------------- /examples/link_layer.py: -------------------------------------------------------------------------------- 1 | """Example of linking layers in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Link multiple layers together 5 | 2. Manage linked layers 6 | 3. Check layer link status 7 | 4. Modify linked layer properties 8 | 9 | Key concepts: 10 | - Layer linking 11 | - Group operations 12 | - Layer management 13 | - Link status 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create test layers 24 | layer1 = doc.artLayers.add() 25 | layer1.name = "Layer 1" 26 | 27 | layer2 = doc.artLayers.add() 28 | layer2.name = "Layer 2" 29 | 30 | layer3 = doc.artLayers.add() 31 | layer3.name = "Layer 3" 32 | 33 | # Link layers 34 | layer1.link(layer2) 35 | layer2.link(layer3) 36 | 37 | # Check link status 38 | ps.echo(f"Layer 1 linked: {layer1.linked}") 39 | ps.echo(f"Layer 2 linked: {layer2.linked}") 40 | ps.echo(f"Layer 3 linked: {layer3.linked}") 41 | 42 | # Move linked layers together 43 | layer1.translate(100, 100) 44 | -------------------------------------------------------------------------------- /examples/list_documents.py: -------------------------------------------------------------------------------- 1 | """List current photoshop all documents.""" 2 | 3 | # Import local modules 4 | import photoshop.api as ps 5 | 6 | 7 | app = ps.Application() 8 | 9 | doc = app.documents[0] 10 | print(doc.name) 11 | 12 | for doc in app.documents: 13 | print(doc.name) 14 | -------------------------------------------------------------------------------- /examples/load_selection.py: -------------------------------------------------------------------------------- 1 | """Example of loading and working with selections in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create and save selections 5 | 2. Load saved selections 6 | 3. Modify selection channels 7 | 4. Combine multiple selections 8 | 9 | Key concepts: 10 | - Selection channels 11 | - Channel operations 12 | - Selection saving/loading 13 | - Selection modification 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create initial selection 24 | doc.selection.select([ 25 | [100, 100], 26 | [300, 100], 27 | [300, 300], 28 | [100, 300] 29 | ]) 30 | 31 | # Save selection to channel 32 | doc.channels.add() 33 | doc.selection.store(doc.channels[-1]) 34 | 35 | # Deselect everything 36 | doc.selection.deselect() 37 | 38 | # Create another selection 39 | doc.selection.select([ 40 | [200, 200], 41 | [400, 200], 42 | [400, 400], 43 | [200, 400] 44 | ]) 45 | 46 | # Save to another channel 47 | doc.channels.add() 48 | doc.selection.store(doc.channels[-1]) 49 | 50 | # Load first selection 51 | doc.selection.load(doc.channels[-2]) 52 | 53 | # Combine with second selection 54 | doc.selection.combine(doc.channels[-1], ps.SelectionType.ExtendSelection) 55 | 56 | # Clean up - delete added channels 57 | doc.channels[-1].remove() 58 | doc.channels[-1].remove() 59 | -------------------------------------------------------------------------------- /examples/move_to_end.py: -------------------------------------------------------------------------------- 1 | """Example of moving layers to different positions in the layer stack. 2 | 3 | This example demonstrates how to: 4 | 1. Move layers within the layer stack 5 | 2. Change layer order 6 | 3. Handle layer positioning 7 | 4. Work with layer groups 8 | 9 | Key concepts: 10 | - Layer ordering 11 | - Layer movement 12 | - Stack manipulation 13 | - Layer hierarchy 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create some test layers 24 | layer1 = doc.artLayers.add() 25 | layer1.name = "Layer 1" 26 | 27 | layer2 = doc.artLayers.add() 28 | layer2.name = "Layer 2" 29 | 30 | layer3 = doc.artLayers.add() 31 | layer3.name = "Layer 3" 32 | 33 | # Move layer1 to the end (bottom) of the stack 34 | layer1.move(doc.layers[-1], ps.ElementPlacement.PlaceAfter) 35 | 36 | # Move layer3 to the beginning (top) of the stack 37 | layer3.move(doc.layers[0], ps.ElementPlacement.PlaceBefore) 38 | 39 | # Move layer2 between layer1 and layer3 40 | layer2.move(layer1, ps.ElementPlacement.PlaceBefore) 41 | -------------------------------------------------------------------------------- /examples/new_document.py: -------------------------------------------------------------------------------- 1 | # Create a new Photoshop document with diminsions 4 inches by 4 inches. 2 | # Import local modules 3 | import photoshop.api as ps 4 | 5 | 6 | # Start up Photoshop application 7 | app = ps.Application() 8 | 9 | start_ruler_units = app.preferences.rulerUnits 10 | 11 | app.preferences.rulerUnits = ps.Units.Pixels 12 | 13 | # Create the document 14 | docRef = app.documents.add(1920, 1080, 72.0, "My New Document") 15 | 16 | # Make sure to set the ruler units prior to creating the document. 17 | app.preferences.rulerUnits = start_ruler_units 18 | -------------------------------------------------------------------------------- /examples/open_psd.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop import Session 3 | import photoshop.api as ps 4 | 5 | 6 | # style 1 7 | app = ps.Application() 8 | app.load("your/psd/or/psb/file_path.psd") 9 | 10 | # style 2 11 | with Session("your/psd/or/psb/file_path.psd", action="open") as ps: 12 | ps.echo(ps.active_document.name) 13 | -------------------------------------------------------------------------------- /examples/operate_channels.py: -------------------------------------------------------------------------------- 1 | """Example of working with channels in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Access and manipulate color channels 5 | 2. Create and modify alpha channels 6 | 3. Work with channel visibility 7 | 4. Handle channel operations 8 | 9 | Key concepts: 10 | - Channel management 11 | - Alpha channels 12 | - Channel visibility 13 | - Color separation 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # List all channels 24 | for channel in doc.channels: 25 | ps.echo(f"Channel: {channel.name}") 26 | 27 | # Create a new alpha channel 28 | new_channel = doc.channels.add() 29 | new_channel.name = "Custom Alpha" 30 | 31 | # Duplicate a channel 32 | if len(doc.channels) > 0: 33 | duplicate = doc.channels[0].duplicate() 34 | duplicate.name = "Channel Copy" 35 | 36 | # Toggle channel visibility 37 | for channel in doc.channels: 38 | channel.visible = True 39 | -------------------------------------------------------------------------------- /examples/operate_layerSet.py: -------------------------------------------------------------------------------- 1 | """Example of working with layer sets (groups) in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create and manage layer groups 5 | 2. Add layers to groups 6 | 3. Organize layer hierarchy 7 | 4. Handle group properties 8 | 9 | Key concepts: 10 | - Layer groups 11 | - Layer organization 12 | - Group properties 13 | - Layer hierarchy 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create a new layer group 24 | main_group = doc.layerSets.add() 25 | main_group.name = "Main Group" 26 | 27 | # Create a nested group 28 | sub_group = main_group.layerSets.add() 29 | sub_group.name = "Sub Group" 30 | 31 | # Add layers to groups 32 | layer1 = main_group.artLayers.add() 33 | layer1.name = "Layer in Main" 34 | 35 | layer2 = sub_group.artLayers.add() 36 | layer2.name = "Layer in Sub" 37 | 38 | # Set group properties 39 | main_group.visible = True 40 | main_group.opacity = 80 41 | 42 | # List layers in groups 43 | for layer in main_group.layers: 44 | ps.echo(f"Layer in main group: {layer.name}") 45 | 46 | for layer in sub_group.layers: 47 | ps.echo(f"Layer in sub group: {layer.name}") 48 | 49 | # Move a layer between groups 50 | layer1.move(sub_group, ps.ElementPlacement.INSIDE) 51 | -------------------------------------------------------------------------------- /examples/photoshop_session.py: -------------------------------------------------------------------------------- 1 | """Add slate information dynamically.""" 2 | 3 | # Import built-in modules 4 | from datetime import datetime 5 | import os 6 | from tempfile import mkdtemp 7 | 8 | # Import third-party modules 9 | import examples._psd_files as psd # Import from examples. 10 | 11 | # Import local modules 12 | from photoshop import Session 13 | 14 | 15 | PSD_FILE = psd.get_psd_files() 16 | file_path = PSD_FILE["slate_template.psd"] 17 | 18 | with Session(file_path, action="open", auto_close=True) as ps: 19 | layer_set = ps.active_document.layerSets.getByName("template") 20 | data = { 21 | "project name": "test_project", 22 | "datetime": datetime.today().strftime("%Y-%m-%d"), 23 | } 24 | for layer in layer_set.layers: 25 | if layer.kind == ps.LayerKind.TextLayer: 26 | layer.textItem.contents = data[layer.textItem.contents.strip()] 27 | 28 | jpg_file = os.path.join(mkdtemp("photoshop-python-api"), "slate.jpg") 29 | ps.active_document.saveAs(jpg_file, ps.JPEGSaveOptions()) 30 | os.startfile(jpg_file) 31 | -------------------------------------------------------------------------------- /examples/replace_images.py: -------------------------------------------------------------------------------- 1 | """Replace the image of the current active layer with a new image.""" 2 | 3 | # Import third-party modules 4 | import examples._psd_files as psd # Import from examples. 5 | 6 | # Import local modules 7 | from photoshop import Session 8 | 9 | 10 | PSD_FILE = psd.get_psd_files() 11 | 12 | 13 | with Session(PSD_FILE["replace_images.psd"], action="open") as ps: 14 | active_layer = ps.active_document.activeLayer 15 | bounds = active_layer.bounds 16 | print(f"current layer {active_layer.name}: {bounds}") 17 | input_file = PSD_FILE["red_100x200.png"] 18 | replace_contents = ps.app.stringIDToTypeID("placedLayerReplaceContents") 19 | desc = ps.ActionDescriptor 20 | idnull = ps.app.charIDToTypeID("null") 21 | desc.putPath(idnull, input_file) 22 | ps.app.executeAction(replace_contents, desc) 23 | 24 | # replaced image. 25 | active_layer = ps.active_document.activeLayer 26 | current_bounds = active_layer.bounds 27 | width = bounds[2] - bounds[0] 28 | height = bounds[3] - bounds[1] 29 | 30 | current_width = current_bounds[2] - current_bounds[0] 31 | current_height = current_bounds[3] - current_bounds[1] 32 | new_size = width / current_width * 100 33 | active_layer.resize(new_size, new_size, ps.AnchorPosition.MiddleCenter) 34 | print(f"current layer {active_layer.name}: {current_bounds}") 35 | -------------------------------------------------------------------------------- /examples/revert_changes.py: -------------------------------------------------------------------------------- 1 | """This example demonstrates how to roll back history.""" 2 | 3 | # Import local modules 4 | from photoshop import Session 5 | 6 | 7 | with Session() as ps: 8 | doc = ps.active_document 9 | old_state = doc.activeHistoryState 10 | print(old_state.name) 11 | doc.artLayers.add() 12 | last_state = doc.activeHistoryState 13 | print(last_state.name) 14 | doc.activeHistoryState = old_state 15 | print(doc.activeHistoryState.name) 16 | -------------------------------------------------------------------------------- /examples/rotate_layer.py: -------------------------------------------------------------------------------- 1 | """Example of rotating layers in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Rotate layers by specific angles 5 | 2. Apply different rotation methods 6 | 3. Handle layer transformations 7 | 4. Preserve layer properties during rotation 8 | 9 | Key concepts: 10 | - Layer rotation 11 | - Transform operations 12 | - Angle calculations 13 | - Layer positioning 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | layer = doc.activeLayer 23 | 24 | # Store original bounds 25 | bounds = layer.bounds 26 | 27 | # Calculate center point 28 | center_x = (bounds[0] + bounds[2]) / 2 29 | center_y = (bounds[1] + bounds[3]) / 2 30 | 31 | # Rotate layer by 45 degrees 32 | layer.rotate(45.0, ps.AnchorPosition.MiddleCenter) 33 | 34 | # Create new layer and rotate it 35 | new_layer = doc.artLayers.add() 36 | new_layer.name = "Rotated Layer" 37 | 38 | # Rotate new layer by 90 degrees 39 | new_layer.rotate(90.0, ps.AnchorPosition.MiddleCenter) 40 | 41 | # Move layer to original center 42 | new_layer.translate(center_x - new_layer.bounds[0], 43 | center_y - new_layer.bounds[1]) 44 | -------------------------------------------------------------------------------- /examples/run_batch.py: -------------------------------------------------------------------------------- 1 | # Import built-in modules 2 | import os 3 | 4 | # Import local modules 5 | from photoshop import Session 6 | 7 | 8 | root = "your/images/root" 9 | files = [] 10 | for name in os.listdir(root): 11 | files.append(os.path.join(root, name)) 12 | with Session() as api: 13 | options = api.BatchOptions() 14 | options.destination = 3 15 | options.destinationFolder = "c:\\test" 16 | api.app.batch(files=files, actionName="Quadrant Colors", actionSet="Default Actions", options=options) 17 | -------------------------------------------------------------------------------- /examples/save_as_pdf.py: -------------------------------------------------------------------------------- 1 | """Example of saving documents as PDF files in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Save documents in PDF format 5 | 2. Configure PDF save options 6 | 3. Handle PDF compression settings 7 | 4. Set PDF compatibility options 8 | 9 | Key concepts: 10 | - PDF export 11 | - Save options 12 | - File compression 13 | - PDF settings 14 | """ 15 | 16 | # Import built-in modules 17 | import os 18 | 19 | # Import local modules 20 | from photoshop import Session 21 | 22 | 23 | with Session() as ps: 24 | doc = ps.active_document 25 | 26 | # Configure PDF save options 27 | pdf_options = ps.PDFSaveOptions() 28 | pdf_options.alphaChannels = True 29 | pdf_options.embedColorProfile = True 30 | pdf_options.embedFonts = True 31 | pdf_options.layers = False 32 | pdf_options.optimizeForWeb = True 33 | pdf_options.view = False 34 | 35 | # Generate output path 36 | output_path = os.path.join(os.path.dirname(__file__), "output.pdf") 37 | 38 | # Save document as PDF 39 | doc.saveAs(output_path, pdf_options, True) 40 | -------------------------------------------------------------------------------- /examples/save_as_tga.py: -------------------------------------------------------------------------------- 1 | """Example of saving documents as TGA files in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Save documents in TGA format 5 | 2. Configure TGA save options 6 | 3. Handle alpha channels 7 | 4. Set compression options 8 | 9 | Key concepts: 10 | - TGA export 11 | - Save options 12 | - Alpha channel handling 13 | - Resolution settings 14 | """ 15 | 16 | # Import built-in modules 17 | import os 18 | 19 | # Import local modules 20 | from photoshop import Session 21 | 22 | 23 | with Session() as ps: 24 | doc = ps.active_document 25 | 26 | # Configure TGA save options 27 | tga_options = ps.TargaSaveOptions() 28 | tga_options.alphaChannels = True 29 | tga_options.resolution = ps.TargaBitsPerPixels.TWENTYFOUR 30 | tga_options.rleCompression = True 31 | 32 | # Generate output path 33 | output_path = os.path.join(os.path.dirname(__file__), "output.tga") 34 | 35 | # Save document as TGA 36 | doc.saveAs(output_path, tga_options, True) 37 | 38 | # Save another version with different settings 39 | tga_options.resolution = ps.TargaBitsPerPixels.THIRTYTWO 40 | output_path_32 = os.path.join(os.path.dirname(__file__), "output_32bit.tga") 41 | doc.saveAs(output_path_32, tga_options, True) 42 | -------------------------------------------------------------------------------- /examples/save_to_psd.py: -------------------------------------------------------------------------------- 1 | """Example of saving documents as PSD files in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Save documents in PSD format 5 | 2. Configure PSD save options 6 | 3. Preserve layer information 7 | 4. Handle compatibility settings 8 | 9 | Key concepts: 10 | - PSD format 11 | - Layer preservation 12 | - Save options 13 | - File compatibility 14 | """ 15 | 16 | # Import built-in modules 17 | import os 18 | 19 | # Import local modules 20 | from photoshop import Session 21 | 22 | 23 | with Session() as ps: 24 | doc = ps.active_document 25 | 26 | # Configure PSD save options 27 | psd_options = ps.PhotoshopSaveOptions() 28 | psd_options.alphaChannels = True 29 | psd_options.annotations = True 30 | psd_options.layers = True 31 | psd_options.spotColors = True 32 | 33 | # Generate output path 34 | output_path = os.path.join(os.path.dirname(__file__), "output.psd") 35 | 36 | # Save document as PSD 37 | doc.saveAs(output_path, psd_options, True) 38 | -------------------------------------------------------------------------------- /examples/selection_stroke.py: -------------------------------------------------------------------------------- 1 | """Example of creating and modifying selection strokes in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Create selections with specific shapes 5 | 2. Apply strokes to selections 6 | 3. Configure stroke options 7 | 4. Work with stroke colors and widths 8 | 9 | Key concepts: 10 | - Selection creation 11 | - Stroke application 12 | - Stroke customization 13 | - Color management 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | 23 | # Create a rectangular selection 24 | doc.selection.select([ 25 | [100, 100], 26 | [400, 100], 27 | [400, 300], 28 | [100, 300] 29 | ]) 30 | 31 | # Create stroke color 32 | stroke_color = ps.SolidColor() 33 | stroke_color.rgb.red = 255 34 | stroke_color.rgb.green = 0 35 | stroke_color.rgb.blue = 0 36 | 37 | # Apply stroke to selection 38 | doc.selection.stroke( 39 | stroke_color, # Color to use 40 | width=2, # Stroke width in pixels 41 | location=ps.StrokeLocation.Inside, 42 | blendMode=ps.ColorBlendMode.Normal, 43 | opacity=100 44 | ) 45 | 46 | # Create circular selection 47 | doc.selection.selectElliptical( 48 | left=200, 49 | top=200, 50 | width=200, 51 | height=200 52 | ) 53 | 54 | # Change stroke color 55 | stroke_color.rgb.blue = 255 56 | 57 | # Apply different stroke 58 | doc.selection.stroke( 59 | stroke_color, 60 | width=5, 61 | location=ps.StrokeLocation.Center, 62 | blendMode=ps.ColorBlendMode.Normal, 63 | opacity=75 64 | ) 65 | 66 | # Clear selection 67 | doc.selection.deselect() 68 | -------------------------------------------------------------------------------- /examples/session_callback.py: -------------------------------------------------------------------------------- 1 | """Example of using callbacks with Photoshop sessions. 2 | 3 | This example demonstrates how to: 4 | 1. Set up session callbacks 5 | 2. Handle Photoshop events 6 | 3. Manage callback execution 7 | 4. Process event data 8 | 9 | Key concepts: 10 | - Event handling 11 | - Callbacks 12 | - Session management 13 | - Event processing 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | def on_close(): 21 | """Callback function for session close event.""" 22 | print("Session is closing") 23 | 24 | 25 | with Session(callback=on_close) as ps: 26 | ps.echo("Working in session...") 27 | -------------------------------------------------------------------------------- /examples/session_document_duplicate.py: -------------------------------------------------------------------------------- 1 | """Example of duplicating documents within a Photoshop session. 2 | 3 | This example demonstrates how to: 4 | 1. Duplicate existing documents 5 | 2. Configure duplication options 6 | 3. Handle document copies 7 | 4. Manage document references 8 | 9 | Key concepts: 10 | - Document duplication 11 | - Session management 12 | - Document handling 13 | - Copy options 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | if len(ps.app.documents) > 0: 22 | # Duplicate active document 23 | doc = ps.active_document.duplicate() 24 | -------------------------------------------------------------------------------- /examples/session_hello_world.py: -------------------------------------------------------------------------------- 1 | """Add slate information dynamically.""" 2 | 3 | # Import built-in modules 4 | import os 5 | from tempfile import mkdtemp 6 | 7 | # Import local modules 8 | from photoshop import Session 9 | 10 | 11 | with Session() as adobe: 12 | doc = adobe.app.documents.add(2000, 2000) 13 | text_color = adobe.SolidColor() 14 | text_color.rgb.red = 255 15 | new_text_layer = doc.artLayers.add() 16 | new_text_layer.kind = adobe.LayerKind.TextLayer 17 | new_text_layer.textItem.contents = "Hello, World!" 18 | new_text_layer.textItem.position = [160, 167] 19 | new_text_layer.textItem.size = 40 20 | new_text_layer.textItem.color = text_color 21 | options = adobe.JPEGSaveOptions(quality=1) 22 | jpg_file = os.path.join(mkdtemp("photoshop-python-api"), "hello_world.jpg") 23 | doc.saveAs(jpg_file, options, asCopy=True) 24 | adobe.app.doJavaScript(f'alert("save to jpg: {jpg_file}")') 25 | -------------------------------------------------------------------------------- /examples/session_new_document.py: -------------------------------------------------------------------------------- 1 | """Action for create new document and print new document name.""" 2 | # Import local modules 3 | from photoshop import Session 4 | 5 | 6 | with Session(action="new_document") as ps: 7 | ps.echo(ps.active_document.name) 8 | -------------------------------------------------------------------------------- /examples/session_smart_sharpen.py: -------------------------------------------------------------------------------- 1 | """This script demonstrates how you can use the action manager to execute the 2 | Emboss filter. 3 | 4 | References: 5 | https://github.com/lohriialo/photoshop-scripting-python/blob/master/SmartSharpen.py 6 | 7 | """ 8 | 9 | # Import third-party modules 10 | import examples._psd_files as psd # Import from examples. 11 | 12 | # Import local modules 13 | from photoshop import Session 14 | 15 | 16 | PSD_FILE = psd.get_psd_files() 17 | file_path = PSD_FILE["layer_comps.psd"] 18 | 19 | with Session(file_path, action="open") as ps: 20 | 21 | def SmartSharpen(inAmount, inRadius, inNoise): 22 | idsmart_sharpen_id = ps.app.stringIDToTypeID(ps.EventID.SmartSharpen) 23 | desc37 = ps.ActionDescriptor() 24 | 25 | idpresetKind = ps.app.stringIDToTypeID(ps.EventID.PresetKind) 26 | idpresetKindType = ps.app.stringIDToTypeID(ps.EventID.PresetKindType) 27 | idpresetKindCustom = ps.app.stringIDToTypeID(ps.EventID.PresetKindCustom) 28 | desc37.putEnumerated(idpresetKind, idpresetKindType, idpresetKindCustom) 29 | idAmnt = ps.app.charIDToTypeID("Amnt") 30 | idPrc = ps.app.charIDToTypeID("Rds ") 31 | desc37.putUnitDouble(idAmnt, idPrc, inAmount) 32 | 33 | idRds = ps.app.charIDToTypeID("Rds ") 34 | idPxl = ps.app.charIDToTypeID("#Pxl") 35 | desc37.putUnitDouble(idRds, idPxl, inRadius) 36 | 37 | idnoiseReduction = ps.app.stringIDToTypeID("noiseReduction") 38 | idPrc = ps.app.charIDToTypeID("#Prc") 39 | desc37.putUnitDouble(idnoiseReduction, idPrc, inNoise) 40 | 41 | idblur = ps.app.charIDToTypeID("blur") 42 | idblurType = ps.app.stringIDToTypeID("blurType") 43 | idGsnB = ps.app.charIDToTypeID("GsnB") 44 | desc37.putEnumerated(idblur, idblurType, idGsnB) 45 | 46 | ps.app.ExecuteAction(idsmart_sharpen_id, desc37) 47 | 48 | docRef = ps.active_document 49 | nlayerSets = docRef.layerSets 50 | nArtLayers = docRef.layerSets.item(nlayerSets.length) 51 | docRef.activeLayer = nArtLayers.artLayers.item(nArtLayers.artLayers.length) 52 | 53 | SmartSharpen(300, 2.0, 20) 54 | -------------------------------------------------------------------------------- /examples/set_active_layer.py: -------------------------------------------------------------------------------- 1 | """ 2 | References: 3 | https://github.com/lohriialo/photoshop-scripting-python/blob/master/ActiveLayer.py 4 | 5 | """ 6 | # Import local modules 7 | import photoshop.api as ps 8 | 9 | 10 | app = ps.Application() 11 | 12 | if app.documents.length < 1: 13 | docRef = app.documents.add() 14 | else: 15 | docRef = app.activeDocument 16 | 17 | if docRef.layers.length < 2: 18 | docRef.artLayers.add() 19 | 20 | activeLayerName = docRef.activeLayer.name 21 | if docRef.activeLayer.name != docRef.layers.item(docRef.layers.length).name: 22 | docRef.activeLayer = docRef.layers.item(docRef.layers.length) 23 | else: 24 | docRef.activeLayer = docRef.layers.item(1) 25 | -------------------------------------------------------------------------------- /examples/smart_sharpen.py: -------------------------------------------------------------------------------- 1 | """Example of applying Smart Sharpen filter in Photoshop. 2 | 3 | This example demonstrates how to: 4 | 1. Apply Smart Sharpen filter with various settings 5 | 2. Configure sharpening parameters 6 | 3. Handle different sharpening methods 7 | 4. Process multiple layers 8 | 9 | Key concepts: 10 | - Smart Sharpen filter 11 | - Filter parameters 12 | - Image enhancement 13 | - Layer processing 14 | """ 15 | 16 | # Import local modules 17 | from photoshop import Session 18 | 19 | 20 | with Session() as ps: 21 | doc = ps.active_document 22 | layer = doc.activeLayer 23 | 24 | # Configure Smart Sharpen options 25 | options = ps.SmartSharpenOptions() 26 | options.amount = 100.0 27 | options.radius = 3.0 28 | options.noiseReduction = 20 29 | options.removeMotionBlur = False 30 | options.angle = 0 31 | options.moreAccurate = True 32 | 33 | # Apply Smart Sharpen 34 | layer.applySmartSharpen( 35 | amount=options.amount, 36 | radius=options.radius, 37 | noiseReduction=options.noiseReduction, 38 | removeMotionBlur=options.removeMotionBlur, 39 | angle=options.angle, 40 | moreAccurate=options.moreAccurate 41 | ) 42 | 43 | # Create a copy with different settings 44 | new_layer = layer.duplicate() 45 | new_layer.name = "Sharp Copy" 46 | 47 | # Apply stronger sharpening 48 | options.amount = 150.0 49 | options.radius = 4.0 50 | new_layer.applySmartSharpen( 51 | amount=options.amount, 52 | radius=options.radius, 53 | noiseReduction=options.noiseReduction, 54 | removeMotionBlur=options.removeMotionBlur, 55 | angle=options.angle, 56 | moreAccurate=options.moreAccurate 57 | ) 58 | -------------------------------------------------------------------------------- /examples/toggle_proof_colors.py: -------------------------------------------------------------------------------- 1 | """Toggle the proof color. 2 | 3 | Like operating in the menu: 4 | **View** > **Proof Colors** (Ctrl + Y) 5 | 6 | """ 7 | # Import local modules 8 | from photoshop import Session 9 | 10 | 11 | with Session() as ps: 12 | ps.app.runMenuItem(ps.app.stringIDToTypeID("toggleProofColors")) 13 | -------------------------------------------------------------------------------- /examples/trim.py: -------------------------------------------------------------------------------- 1 | """A trim example.""" 2 | 3 | # Import third-party modules 4 | import examples._psd_files as psd # Import from examples. 5 | 6 | # Import local modules 7 | from photoshop import Session 8 | 9 | 10 | PSD_FILE = psd.get_psd_files() 11 | example_file = PSD_FILE["trim.psd"] 12 | with Session(example_file, action="open") as ps: 13 | ps.active_document.trim(ps.TrimType.TopLeftPixel, True, True, True, True) 14 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Photoshop Python API 2 | repo_url: https://github.com/loonghao/photoshop-python-api 3 | repo_name: github 4 | site_author: longhao 5 | remote_branch: master 6 | copyright: "Copyright (c) 2019 Long Hao" 7 | features: 8 | - content.code.annotate 9 | - navigation.indexes 10 | - navigation.sections 11 | - navigation.tabs 12 | - navigation.top 13 | - navigation.tracking 14 | - search.highlight 15 | - search.share 16 | - search.suggest 17 | - toc.follow 18 | 19 | docs_dir: docs 20 | 21 | palette: 22 | scheme: dracula 23 | primary: deep purple # Primary colors 24 | accent: deep purple # accent color 25 | 26 | nav: 27 | - Home: 28 | - Overview: index.md 29 | - Changelog: changelog.md 30 | - Examples: examples.md 31 | # defer to gen-files + literate-nav 32 | - Code Reference: reference/ 33 | - 'Issue Tracker': 'https://github.com/loonghao/photoshop-python-api/issues' 34 | 35 | 36 | markdown_extensions: 37 | - pymdownx.extra 38 | - admonition 39 | - def_list 40 | - footnotes 41 | - meta 42 | - toc 43 | - pymdownx.arithmatex 44 | - pymdownx.betterem 45 | - pymdownx.caret 46 | - pymdownx.critic 47 | - pymdownx.details 48 | - pymdownx.snippets 49 | - pymdownx.highlight 50 | - pymdownx.emoji: 51 | emoji_index: !!python/name:materialx.emoji.twemoji 52 | emoji_generator: !!python/name:materialx.emoji.to_svg 53 | - pymdownx.inlinehilite 54 | - pymdownx.magiclink 55 | - pymdownx.mark 56 | - pymdownx.smartsymbols 57 | - pymdownx.tabbed: 58 | alternate_style: true 59 | - pymdownx.superfences: 60 | custom_fences: 61 | - name: mermaid 62 | class: mermaid 63 | format: !!python/name:pymdownx.superfences.fence_code_format 64 | - pymdownx.tasklist: 65 | custom_checkbox: true 66 | - pymdownx.tilde 67 | - attr_list 68 | - md_in_html 69 | 70 | theme: 71 | name: material 72 | 73 | plugins: 74 | - include-markdown 75 | - search 76 | - same-dir 77 | - autorefs 78 | - autolinks 79 | - gen-files: 80 | scripts: 81 | - docs/gen_api_nav.py 82 | - docs/gen_examples.py 83 | - literate-nav: 84 | nav_file: SUMMARY.md 85 | - git-revision-date-localized: 86 | type: timeago 87 | fallback_to_build_date: true 88 | enable_creation_date: true 89 | enabled: true 90 | - mkdocstrings: 91 | handlers: 92 | python: 93 | import: 94 | - https://docs.python.org/3/objects.inv 95 | rendering: 96 | show_root_heading: true 97 | show_source: false 98 | docstring_section_style: list 99 | members_order: alphabetical 100 | show_category_heading: no 101 | merge_init_into_class: yes 102 | how_submodules: no 103 | selection: 104 | docstring_style: google 105 | docstring_options: 106 | ignore_init_summary: yes 107 | - minify: 108 | minify_html: true 109 | minify_js: true 110 | minify_css: true 111 | htmlmin_opts: 112 | remove_comments: true 113 | - mkdocs_pymdownx_material_extras 114 | -------------------------------------------------------------------------------- /photoshop/__init__.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.session import Session 3 | 4 | 5 | __all__ = ["Session"] 6 | -------------------------------------------------------------------------------- /photoshop/__version__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.24.1" 2 | -------------------------------------------------------------------------------- /photoshop/api/__init__.py: -------------------------------------------------------------------------------- 1 | """Python API for Photoshop.""" 2 | # Import local modules 3 | from photoshop.api import constants 4 | from photoshop.api.action_descriptor import ActionDescriptor 5 | from photoshop.api.action_list import ActionList 6 | from photoshop.api.action_reference import ActionReference 7 | from photoshop.api.application import Application 8 | from photoshop.api.batch_options import BatchOptions 9 | from photoshop.api.colors import CMYKColor 10 | from photoshop.api.colors import GrayColor 11 | from photoshop.api.colors import HSBColor 12 | from photoshop.api.colors import LabColor 13 | from photoshop.api.colors import RGBColor 14 | from photoshop.api.enumerations import * # noqa: F403 15 | from photoshop.api.errors import PhotoshopPythonAPICOMError 16 | from photoshop.api.errors import PhotoshopPythonAPIError 17 | from photoshop.api.event_id import EventID 18 | from photoshop.api.open_options import EPSOpenOptions 19 | from photoshop.api.save_options import BMPSaveOptions 20 | from photoshop.api.save_options import EPSSaveOptions 21 | from photoshop.api.save_options import ExportOptionsSaveForWeb 22 | from photoshop.api.save_options import GIFSaveOptions 23 | from photoshop.api.save_options import JPEGSaveOptions 24 | from photoshop.api.save_options import PDFSaveOptions 25 | from photoshop.api.save_options import PNGSaveOptions 26 | from photoshop.api.save_options import PhotoshopSaveOptions 27 | from photoshop.api.save_options import TargaSaveOptions 28 | from photoshop.api.save_options import TiffSaveOptions 29 | from photoshop.api.solid_color import SolidColor 30 | from photoshop.api.text_item import TextItem 31 | 32 | 33 | __all__ = [ # noqa: F405 34 | "ActionDescriptor", 35 | "ActionReference", 36 | "ActionList", 37 | "Application", 38 | "BatchOptions", 39 | "constants", 40 | "enumerations", 41 | "PhotoshopPythonAPIError", 42 | "PhotoshopPythonAPICOMError", 43 | "CMYKColor", 44 | "GrayColor", 45 | "HSBColor", 46 | "LabColor", 47 | "RGBColor", 48 | "SolidColor", 49 | "EventID", 50 | "BMPSaveOptions", 51 | "GIFSaveOptions", 52 | "JPEGSaveOptions", 53 | "PDFSaveOptions", 54 | "ExportOptionsSaveForWeb", 55 | "PNGSaveOptions", 56 | "PhotoshopSaveOptions", 57 | "TiffSaveOptions", 58 | "TargaSaveOptions", 59 | "EPSOpenOptions", 60 | "EPSSaveOptions", 61 | "TextItem", 62 | ] 63 | -------------------------------------------------------------------------------- /photoshop/api/_active_layer.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class ActiveLayer(Photoshop): 6 | """The selected layer.""" 7 | 8 | def __int__(self): 9 | super().__init__() 10 | 11 | @property 12 | def name(self) -> str: 13 | """The name of the layer.""" 14 | return self.active_layer.Typename 15 | 16 | def add(self): 17 | """Adds an element.""" 18 | self.app.ActiveDocument.ArtLayers.Add() 19 | -------------------------------------------------------------------------------- /photoshop/api/_artlayers.py: -------------------------------------------------------------------------------- 1 | # Import third-party modules 2 | from comtypes import ArgumentError 3 | 4 | # Import local modules 5 | from photoshop.api._artlayer import ArtLayer 6 | from photoshop.api._core import Photoshop 7 | from photoshop.api.errors import PhotoshopPythonAPIError 8 | 9 | 10 | # pylint: disable=too-many-public-methods 11 | class ArtLayers(Photoshop): 12 | """The collection of art layer objects in the document.""" 13 | 14 | def __init__(self, parent): 15 | super().__init__(parent=parent) 16 | self._flag_as_method( 17 | "add", 18 | ) 19 | 20 | @property 21 | def _layers(self): 22 | return list(self.app) 23 | 24 | def __len__(self): 25 | return self.length 26 | 27 | def __iter__(self): 28 | for layer in self.app: 29 | yield layer 30 | 31 | def __getitem__(self, key: str): 32 | """Access a given ArtLayer using dictionary key lookup.""" 33 | try: 34 | return ArtLayer(self.app[key]) 35 | except ArgumentError: 36 | raise PhotoshopPythonAPIError(f'Could not find an artLayer named "{key}"') 37 | 38 | @property 39 | def length(self): 40 | return len(self._layers) 41 | 42 | @property 43 | def parent(self): 44 | return self.app.parent 45 | 46 | @property 47 | def typename(self): 48 | return self.app.typename 49 | 50 | def add(self): 51 | """Adds an element.""" 52 | return ArtLayer(self.app.add()) 53 | 54 | def getByIndex(self, index: int): 55 | """Access ArtLayer using list index lookup.""" 56 | return ArtLayer(self._layers[index]) 57 | 58 | def getByName(self, name: str) -> ArtLayer: 59 | """Get the first element in the collection with the provided name. 60 | 61 | Raises: 62 | PhotoshopPythonAPIError: Could not find a artLayer. 63 | """ 64 | for layer in self.app: 65 | if layer.name == name: 66 | return ArtLayer(layer) 67 | raise PhotoshopPythonAPIError(f'Could not find an artLayer named "{name}"') 68 | 69 | def removeAll(self): 70 | """Deletes all elements.""" 71 | for layer in self.app: 72 | ArtLayer(layer).remove() 73 | -------------------------------------------------------------------------------- /photoshop/api/_channel.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | # pylint: disable=too-many-public-methods 6 | class Channel(Photoshop): 7 | def __init__(self, parent): 8 | super().__init__(parent=parent) 9 | self._flag_as_method( 10 | "duplicate", 11 | "merge", 12 | ) 13 | 14 | @property 15 | def color(self): 16 | return self.app.color 17 | 18 | @color.setter 19 | def color(self, value): 20 | self.app.color = value 21 | 22 | @property 23 | def histogram(self): 24 | return self.app.histogram 25 | 26 | @histogram.setter 27 | def histogram(self, value): 28 | self.app.histogram = value 29 | 30 | @property 31 | def kind(self): 32 | return self.app.kind 33 | 34 | @kind.setter 35 | def kind(self, value): 36 | self.app.kind = value 37 | 38 | @property 39 | def opacity(self): 40 | return self.app.opacity 41 | 42 | @opacity.setter 43 | def opacity(self, value): 44 | self.app.opacity = value 45 | 46 | @property 47 | def visible(self): 48 | return self.app.visible 49 | 50 | @visible.setter 51 | def visible(self, value): 52 | self.app.visible = value 53 | 54 | @property 55 | def name(self): 56 | return self.app.name 57 | 58 | def duplicate(self, targetDocument=None): 59 | self.app.duplicate(targetDocument) 60 | 61 | def merge(self): 62 | self.app.merge() 63 | 64 | def remove(self): 65 | channel = f'app.activeDocument.channels.getByName("{self.name}")' 66 | self.eval_javascript(f"{channel}.remove()") 67 | -------------------------------------------------------------------------------- /photoshop/api/_channels.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._channel import Channel 3 | from photoshop.api._core import Photoshop 4 | from photoshop.api.errors import PhotoshopPythonAPIError 5 | 6 | 7 | # pylint: disable=too-many-public-methods 8 | class Channels(Photoshop): 9 | def __init__(self, parent): 10 | super().__init__(parent=parent) 11 | self._flag_as_method( 12 | "add", 13 | "removeAll", 14 | ) 15 | 16 | @property 17 | def _channels(self): 18 | return list(self.app) 19 | 20 | def __len__(self): 21 | return self.length 22 | 23 | def __iter__(self): 24 | for layer in self.app: 25 | yield layer 26 | 27 | def __getitem__(self, item): 28 | return self.app[item] 29 | 30 | @property 31 | def length(self): 32 | return len(self._channels) 33 | 34 | def add(self): 35 | self.app.add() 36 | 37 | def removeAll(self): 38 | self.app.removeAll() 39 | 40 | def getByName(self, name) -> Channel: 41 | for channel in self._channels: 42 | if channel.name == name: 43 | return Channel(channel) 44 | raise PhotoshopPythonAPIError(f'Could not find a channel named "{name}"') 45 | -------------------------------------------------------------------------------- /photoshop/api/_documentinfo.py: -------------------------------------------------------------------------------- 1 | """Metadata about a document object. 2 | 3 | These values can be set by selecting File > File Info in the Adobe Photoshop 4 | application. 5 | 6 | """ 7 | 8 | # Import built-in modules 9 | from pprint import pformat 10 | 11 | # Import local modules 12 | from photoshop.api._core import Photoshop 13 | 14 | 15 | # pylint: disable=too-many-public-methods 16 | class DocumentInfo(Photoshop): 17 | """Metadata about a document object.""" 18 | 19 | def __init__(self, parent): 20 | super().__init__(parent=parent) 21 | 22 | def __str__(self): 23 | return pformat( 24 | { 25 | "author": self.author, 26 | "authorPosition": self.authorPosition, 27 | "caption": self.caption, 28 | "captionWriter": self.captionWriter, 29 | "category": self.category, 30 | "city": self.city, 31 | "country": self.country, 32 | "copyrightNotice": self.copyrightNotice, 33 | "copyrighted": self.copyrighted, 34 | "creationDate": self.creationDate, 35 | "credit": self.credit, 36 | "exif": self.exif, 37 | "headline": self.headline, 38 | "instructions": self.instructions, 39 | "jobName": self.jobName, 40 | "keywords": self.keywords, 41 | "provinceState": self.provinceState, 42 | "source": self.source, 43 | "ownerUrl": self.ownerUrl, 44 | "supplementalCategories": self.supplementalCategories, 45 | "title": self.title, 46 | "transmissionReference": self.transmissionReference, 47 | "urgency": self.urgency, 48 | } 49 | ) 50 | 51 | @property 52 | def author(self): 53 | """str: The author.""" 54 | return self.app.author 55 | 56 | @author.setter 57 | def author(self, name): 58 | self.app.author = name 59 | 60 | @property 61 | def authorPosition(self): 62 | """str:The author’s position.""" 63 | return self.app.authorPosition 64 | 65 | @authorPosition.setter 66 | def authorPosition(self, name): 67 | self.app.authorPosition = name 68 | 69 | @property 70 | def caption(self): 71 | return self.app.caption 72 | 73 | @caption.setter 74 | def caption(self, name): 75 | self.app.caption = name 76 | 77 | @property 78 | def captionWriter(self): 79 | return self.app.captionWriter 80 | 81 | @captionWriter.setter 82 | def captionWriter(self, name): 83 | self.app.captionWriter = name 84 | 85 | @property 86 | def category(self): 87 | """str: The document category.""" 88 | return self.app.category 89 | 90 | @category.setter 91 | def category(self, name): 92 | self.app.category = name 93 | 94 | @property 95 | def city(self): 96 | return self.app.city 97 | 98 | @city.setter 99 | def city(self, city_name): 100 | self.app.city = city_name 101 | 102 | @property 103 | def copyrightNotice(self): 104 | """str: The copyright notice.""" 105 | return self.app.copyrightNotice 106 | 107 | @copyrightNotice.setter 108 | def copyrightNotice(self, name): 109 | self.app.copyrightNotice = name 110 | 111 | @property 112 | def copyrighted(self): 113 | """str: The copyright status.""" 114 | return self.app.copyrighted 115 | 116 | @copyrighted.setter 117 | def copyrighted(self, info): 118 | self.app.copyrighted = info 119 | 120 | @property 121 | def country(self): 122 | return self.app.country 123 | 124 | @country.setter 125 | def country(self, name): 126 | self.app.country = name 127 | 128 | @property 129 | def creationDate(self): 130 | return self.app.creationDate 131 | 132 | @creationDate.setter 133 | def creationDate(self, name): 134 | self.app.creationDate = name 135 | 136 | @property 137 | def credit(self): 138 | """str: The author credit.""" 139 | return self.app.credit 140 | 141 | @credit.setter 142 | def credit(self, value): 143 | self.app.credit = value 144 | 145 | @property 146 | def exif(self): 147 | return self.app.exif 148 | 149 | @exif.setter 150 | def exif(self, info): 151 | self.app.exif = info 152 | 153 | @property 154 | def headline(self): 155 | return self.app.headline 156 | 157 | @headline.setter 158 | def headline(self, value): 159 | self.app.headline = value 160 | 161 | @property 162 | def instructions(self): 163 | return self.app.instructions 164 | 165 | @instructions.setter 166 | def instructions(self, value): 167 | self.app.instructions = value 168 | 169 | @property 170 | def jobName(self): 171 | return self.app.jobName 172 | 173 | @jobName.setter 174 | def jobName(self, job): 175 | self.app.jobName = job 176 | 177 | @property 178 | def keywords(self): 179 | return self.app.keywords 180 | 181 | @keywords.setter 182 | def keywords(self, words): 183 | self.app.keywords = words 184 | 185 | @property 186 | def ownerUrl(self): 187 | return self.app.ownerUrl 188 | 189 | @ownerUrl.setter 190 | def ownerUrl(self, url): 191 | self.app.ownerUrl = url 192 | 193 | @property 194 | def provinceState(self): 195 | """str: The state or province.""" 196 | return self.app.provinceState 197 | 198 | @provinceState.setter 199 | def provinceState(self, state_name): 200 | self.app.provinceState = state_name 201 | 202 | @property 203 | def source(self): 204 | return self.app.source 205 | 206 | @source.setter 207 | def source(self, source_name): 208 | self.app.source = source_name 209 | 210 | @property 211 | def supplementalCategories(self): 212 | """str: Other categories.""" 213 | return self.app.supplementalCategories 214 | 215 | @supplementalCategories.setter 216 | def supplementalCategories(self, info): 217 | self.app.supplementalCategories = info 218 | 219 | @property 220 | def title(self): 221 | return self.app.title 222 | 223 | @title.setter 224 | def title(self, name): 225 | self.app.title = name 226 | 227 | @property 228 | def transmissionReference(self): 229 | """str: The transmission reference.""" 230 | return self.app.transmissionReference 231 | 232 | @transmissionReference.setter 233 | def transmissionReference(self, reference): 234 | self.app.transmissionReference = reference 235 | 236 | @property 237 | def urgency(self): 238 | """The document urgency.""" 239 | return self.app.urgency 240 | 241 | @urgency.setter 242 | def urgency(self, status): 243 | self.app.urgency = status 244 | -------------------------------------------------------------------------------- /photoshop/api/_documents.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | from photoshop.api._document import Document 4 | from photoshop.api.enumerations import BitsPerChannelType 5 | from photoshop.api.enumerations import DocumentFill 6 | from photoshop.api.enumerations import NewDocumentMode 7 | from photoshop.api.errors import PhotoshopPythonAPIError 8 | 9 | 10 | # pylint: disable=too-many-public-methods, too-many-arguments 11 | class Documents(Photoshop): 12 | """The collection of open documents.""" 13 | 14 | def __init__(self, parent): 15 | super().__init__(parent=parent) 16 | self._flag_as_method("add") 17 | 18 | def __len__(self) -> int: 19 | return self.length 20 | 21 | def add( 22 | self, 23 | width: int = 960, 24 | height: int = 540, 25 | resolution: float = 72.0, 26 | name: str = None, 27 | mode: int = NewDocumentMode.NewRGB, 28 | initialFill: int = DocumentFill.White, 29 | pixelAspectRatio: float = 1.0, 30 | bitsPerChannel: int = BitsPerChannelType.Document8Bits, 31 | colorProfileName: str = None, 32 | ) -> Document: 33 | """Creates a new document object and adds it to this collections. 34 | 35 | Args: 36 | width (int): The width of the document. 37 | height (int): The height of the document. 38 | resolution (int): The resolution of the document (in pixels per inch) 39 | name (str): The name of the document. 40 | mode (): The document mode. 41 | initialFill : The initial fill of the document. 42 | pixelAspectRatio: The initial pixel aspect ratio of the document. 43 | Default is `1.0`, the range is `0.1-10.00`. 44 | bitsPerChannel: The number of bits per channel. 45 | colorProfileName: The name of color profile for document. 46 | 47 | Returns: 48 | .Document: Document instance. 49 | 50 | """ 51 | return Document( 52 | self.app.add( 53 | width, 54 | height, 55 | resolution, 56 | name, 57 | mode, 58 | initialFill, 59 | pixelAspectRatio, 60 | bitsPerChannel, 61 | colorProfileName, 62 | ) 63 | ) 64 | 65 | def __iter__(self) -> Document: 66 | for doc in self.app: 67 | self.adobe.activeDocument = doc 68 | yield Document(doc) 69 | 70 | def __getitem__(self, item) -> Document: 71 | try: 72 | return Document(self.app[item]) 73 | except IndexError: 74 | raise PhotoshopPythonAPIError("Currently Photoshop did not find Documents.") 75 | 76 | @property 77 | def length(self) -> int: 78 | return len(list(self.app)) 79 | 80 | def getByName(self, document_name: str) -> Document: 81 | """Get document by given document name.""" 82 | for doc in self.app: 83 | if doc.name == document_name: 84 | return Document(doc) 85 | raise PhotoshopPythonAPIError(f'Could not find a document named "{document_name}"') 86 | -------------------------------------------------------------------------------- /photoshop/api/_layerComp.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class LayerComp(Photoshop): 6 | """A snapshot of a state of the layers in a document (can be used to view different page layouts or compostions).""" 7 | 8 | def __init__(self, parent): 9 | super().__init__(parent=parent) 10 | self._flag_as_method( 11 | "apply", 12 | "recapture", 13 | "remove", 14 | "resetfromComp", 15 | ) 16 | 17 | def __len__(self): 18 | return self.length 19 | 20 | @property 21 | def appearance(self): 22 | return self.app.appearance 23 | 24 | @appearance.setter 25 | def appearance(self, value): 26 | self.app.appearance = value 27 | 28 | @property 29 | def childLayerCompState(self): 30 | return self.app.childLayerCompState 31 | 32 | @childLayerCompState.setter 33 | def childLayerCompState(self, value): 34 | self.app.childLayerCompState = value 35 | 36 | @property 37 | def comment(self): 38 | return self.app.comment 39 | 40 | @comment.setter 41 | def comment(self, text): 42 | self.app.comment = text 43 | 44 | @property 45 | def name(self): 46 | return self.app.name 47 | 48 | @name.setter 49 | def name(self, text): 50 | self.app.name = text 51 | 52 | @property 53 | def parent(self): 54 | return self.app.parent 55 | 56 | @property 57 | def position(self): 58 | return self.app.position 59 | 60 | @position.setter 61 | def position(self, value): 62 | self.app.position = value 63 | 64 | @property 65 | def selected(self): 66 | """True if the layer comp is currently selected.""" 67 | return self.app.selected 68 | 69 | @selected.setter 70 | def selected(self, value): 71 | self.app.selected = value 72 | 73 | @property 74 | def typename(self): 75 | return self.app.typename 76 | 77 | @property 78 | def visibility(self): 79 | """True to use layer visibility settings.""" 80 | return self.app.visibility 81 | 82 | @visibility.setter 83 | def visibility(self, value): 84 | self.app.visibility = value 85 | 86 | def apply(self): 87 | """Applies the layer comp to the document.""" 88 | self.app.apply() 89 | 90 | def recapture(self): 91 | """Recaptures the current layer state(s) for this layer comp.""" 92 | self.app.recapture() 93 | 94 | def remove(self): 95 | """Deletes the layerComp object.""" 96 | self.app.remove() 97 | 98 | def resetfromComp(self): 99 | """Resets the layer comp state to thedocument state.""" 100 | self.app.resetfromComp() 101 | -------------------------------------------------------------------------------- /photoshop/api/_layerComps.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | from photoshop.api._layerComp import LayerComp 4 | from photoshop.api.errors import PhotoshopPythonAPIError 5 | 6 | 7 | class LayerComps(Photoshop): 8 | """The layer comps collection in this document.""" 9 | 10 | def __init__(self, parent): 11 | super().__init__(parent=parent) 12 | self._flag_as_method( 13 | "add", 14 | "removeAll", 15 | ) 16 | 17 | def __len__(self): 18 | return self.length 19 | 20 | @property 21 | def length(self): 22 | return len(self._layers) 23 | 24 | @property 25 | def _layers(self): 26 | return list(self.app) 27 | 28 | @property 29 | def parent(self): 30 | return self.app.parent 31 | 32 | @property 33 | def typename(self): 34 | return self.app.typename 35 | 36 | def add( 37 | self, 38 | name, 39 | comment="No Comment.", 40 | appearance=True, 41 | position=True, 42 | visibility=True, 43 | childLayerCompStat=False, 44 | ): 45 | return LayerComp(self.app.add(name, comment, appearance, position, visibility, childLayerCompStat)) 46 | 47 | def getByName(self, name): 48 | for layer in self._layers: 49 | if layer.name == name: 50 | return LayerComp(layer) 51 | raise PhotoshopPythonAPIError(f'Could not find a layer named "{name}"') 52 | 53 | def removeAll(self): 54 | self.app.removeAll() 55 | 56 | def __iter__(self): 57 | for layer in self._layers: 58 | yield LayerComp(layer) 59 | -------------------------------------------------------------------------------- /photoshop/api/_layerSet.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._artlayer import ArtLayer 3 | from photoshop.api._artlayers import ArtLayers 4 | from photoshop.api._core import Photoshop 5 | from photoshop.api._layers import Layers 6 | from photoshop.api.enumerations import AnchorPosition 7 | from photoshop.api.enumerations import BlendMode 8 | 9 | 10 | class LayerSet(Photoshop): 11 | """A group of layer objects, which can include art layer objects and other (nested) layer set objects. 12 | 13 | A single command or set of commands manipulates all layers in a layer set object. 14 | 15 | """ 16 | 17 | def __init__(self, parent): 18 | super().__init__(parent=parent) 19 | self._flag_as_method( 20 | "merge", 21 | "duplicate", 22 | "add", 23 | "delete", 24 | "link", 25 | "move", 26 | "resize", 27 | "rotate", 28 | "translate", 29 | "unlink", 30 | ) 31 | 32 | @property 33 | def allLocked(self): 34 | return self.app.allLocked 35 | 36 | @allLocked.setter 37 | def allLocked(self, value): 38 | self.app.allLocked = value 39 | 40 | @property 41 | def artLayers(self): 42 | return ArtLayers(self.app.artLayers) 43 | 44 | @property 45 | def blendMode(self): 46 | return BlendMode(self.app.blendMode) 47 | 48 | @property 49 | def bounds(self): 50 | """The bounding rectangle of the layer set.""" 51 | return self.app.bounds 52 | 53 | @property 54 | def enabledChannels(self): 55 | return self.app.enabledChannels 56 | 57 | @enabledChannels.setter 58 | def enabledChannels(self, value): 59 | self.app.enabledChannels = value 60 | 61 | @property 62 | def layers(self): 63 | return Layers(self.app.layers) 64 | 65 | @property 66 | def layerSets(self): 67 | # pylint: disable=import-outside-toplevel 68 | from ._layerSets import LayerSets 69 | 70 | return LayerSets(self.app.layerSets) 71 | 72 | @property 73 | def linkedLayers(self): 74 | """The layers linked to this layerSet object.""" 75 | return self.app.linkedLayers or [] 76 | 77 | @property 78 | def name(self) -> str: 79 | return self.app.name 80 | 81 | @name.setter 82 | def name(self, value): 83 | """The name of this layer set.""" 84 | self.app.name = value 85 | 86 | @property 87 | def opacity(self): 88 | """The master opacity of the set.""" 89 | return round(self.app.opacity) 90 | 91 | @opacity.setter 92 | def opacity(self, value): 93 | self.app.opacity = value 94 | 95 | @property 96 | def parent(self): 97 | return self.app.parent 98 | 99 | @property 100 | def visible(self): 101 | return self.app.visible 102 | 103 | @visible.setter 104 | def visible(self, value): 105 | self.app.visible = value 106 | 107 | def duplicate(self, relativeObject=None, insertionLocation=None): 108 | return LayerSet(self.app.duplicate(relativeObject, insertionLocation)) 109 | 110 | def link(self, with_layer): 111 | self.app.link(with_layer) 112 | 113 | def add(self): 114 | """Adds an element.""" 115 | self.app.add() 116 | 117 | def merge(self) -> ArtLayer: 118 | """Merges the layer set.""" 119 | return ArtLayer(self.app.merge()) 120 | 121 | def move(self, relativeObject, insertionLocation): 122 | self.app.move(relativeObject, insertionLocation) 123 | 124 | def remove(self): 125 | """Remove this layer set from the document.""" 126 | self.app.delete() 127 | 128 | def resize(self, horizontal=None, vertical=None, anchor: AnchorPosition = None): 129 | self.app.resize(horizontal, vertical, anchor) 130 | 131 | def rotate(self, angle, anchor=None): 132 | self.app.rotate(angle, anchor) 133 | 134 | def translate(self, delta_x, delta_y): 135 | """Moves the position relative to its current position.""" 136 | self.app.translate(delta_x, delta_y) 137 | 138 | def unlink(self): 139 | """Unlinks the layer set.""" 140 | self.app.unlink() 141 | 142 | def __iter__(self): 143 | for layer in self.app: 144 | yield layer 145 | -------------------------------------------------------------------------------- /photoshop/api/_layerSets.py: -------------------------------------------------------------------------------- 1 | # Import third-party modules 2 | from comtypes import ArgumentError 3 | 4 | # Import local modules 5 | from photoshop.api._core import Photoshop 6 | from photoshop.api._layerSet import LayerSet 7 | from photoshop.api.errors import PhotoshopPythonAPIError 8 | 9 | 10 | class LayerSets(Photoshop): 11 | """The layer sets collection in the document.""" 12 | 13 | def __init__(self, parent): 14 | super().__init__(parent=parent) 15 | self._flag_as_method( 16 | "add", 17 | "item", 18 | "removeAll", 19 | ) 20 | 21 | def __len__(self): 22 | return self.length 23 | 24 | def __iter__(self): 25 | for layer_set in self.app: 26 | yield layer_set 27 | 28 | def __getitem__(self, key: str): 29 | """Access a given LayerSet using dictionary key lookup.""" 30 | try: 31 | return LayerSet(self.app[key]) 32 | except ArgumentError: 33 | raise PhotoshopPythonAPIError(f'Could not find a LayerSet named "{key}"') 34 | 35 | @property 36 | def _layerSets(self): 37 | return list(self.app) 38 | 39 | @property 40 | def length(self) -> int: 41 | """Number of elements in the collection.""" 42 | return len(self._layerSets) 43 | 44 | def add(self): 45 | return LayerSet(self.app.add()) 46 | 47 | def item(self, index: int) -> LayerSet: 48 | return LayerSet(self.app.item(index)) 49 | 50 | def removeAll(self): 51 | self.app.removeAll() 52 | 53 | def getByIndex(self, index: int): 54 | """Access LayerSet using list index lookup.""" 55 | return LayerSet(self._layerSets[index]) 56 | 57 | def getByName(self, name: str) -> LayerSet: 58 | """Get the first element in the collection with the provided name.""" 59 | for layer in self.app: 60 | if name == layer.name: 61 | return LayerSet(layer) 62 | raise PhotoshopPythonAPIError(f'Could not find a LayerSet named "{name}"') 63 | -------------------------------------------------------------------------------- /photoshop/api/_layers.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._artlayer import ArtLayer 3 | from photoshop.api._core import Photoshop 4 | from photoshop.api.errors import PhotoshopPythonAPIError 5 | 6 | 7 | # pylint: disable=too-many-public-methods 8 | class Layers(Photoshop): 9 | """The layers collection in the document.""" 10 | 11 | def __init__(self, parent): 12 | super().__init__(parent=parent) 13 | self._flag_as_method( 14 | "add", 15 | "item", 16 | ) 17 | 18 | @property 19 | def _layers(self): 20 | return list(self.app) 21 | 22 | def __len__(self): 23 | return self.length 24 | 25 | def __getitem__(self, key): 26 | item = self._layers[key] 27 | return ArtLayer(item) 28 | 29 | @property 30 | def length(self): 31 | return len(self._layers) 32 | 33 | def removeAll(self): 34 | """Deletes all elements.""" 35 | for layer in self.app: 36 | ArtLayer(layer).remove() 37 | 38 | def item(self, index): 39 | return ArtLayer(self.app.item(index)) 40 | 41 | def __iter__(self): 42 | for layer in self._layers: 43 | yield ArtLayer(layer) 44 | 45 | def getByName(self, name: str) -> ArtLayer: 46 | """Get the first element in the collection with the provided name.""" 47 | for layer in self.app: 48 | if layer.name == name: 49 | return ArtLayer(layer) 50 | raise PhotoshopPythonAPIError("X") 51 | -------------------------------------------------------------------------------- /photoshop/api/_measurement_log.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class MeasurementLog(Photoshop): 6 | """The log of measurements taken.""" 7 | 8 | def __init__(self, parent): 9 | super().__init__(parent=parent) 10 | self._flag_as_method( 11 | "exportMeasurements", 12 | "deleteMeasurements", 13 | ) 14 | 15 | def exportMeasurements(self, file_path: str, range_: int = None, data_point=None): 16 | if data_point is None: 17 | data_point = [] 18 | self.app.exportMeasurements(file_path, range_, data_point) 19 | 20 | def deleteMeasurements(self, range_: int): 21 | self.app.deleteMeasurements(range_) 22 | -------------------------------------------------------------------------------- /photoshop/api/_notifier.py: -------------------------------------------------------------------------------- 1 | """ 2 | The collection of Notifier objects in the document. Access through the 3 | Application.notifiers collection property. For example: 4 | var notRef = app.notifiers.add("OnClickGoButton", eventFile) 5 | Notifiers must be enabled using the Application.notifiersEnabled property 6 | 7 | """ 8 | # Import built-in modules 9 | from pathlib import Path 10 | 11 | # Import local modules 12 | from photoshop.api._core import Photoshop 13 | 14 | 15 | class Notifier(Photoshop): 16 | def __init__(self, parent=None): 17 | super().__init__() 18 | self._flag_as_method( 19 | "remove", 20 | ) 21 | 22 | @property 23 | def event(self): 24 | """The event identifier, a four-character code or a unique string.""" 25 | return self.app.event 26 | 27 | @property 28 | def eventClass(self): 29 | """The class identifier, a four-character code or a unique string. 30 | 31 | When an event applies to multiple types of objects, use this 32 | propery to distinguish which object this notifier applies to. 33 | For example, the Make event ("Mk ") can apply to 34 | documents ("Dcmn"), channels ("Chnl") and other objects. 35 | 36 | """ 37 | return self.app.eventClass 38 | 39 | @property 40 | def eventFile(self) -> Path: 41 | """The path to the file to execute when the event occurs and 42 | activates the notifier.""" 43 | return Path(self.app.eventFile) 44 | 45 | def remove(self): 46 | """Deletes this object. 47 | 48 | You can also remove a Notifier object 49 | from the Script Events Manager 50 | drop-down list by deleting the file named 51 | Script Events Manager.xml from the 52 | Photoshop preferences folder. See Adobe 53 | Photoshop CC help for more information. 54 | 55 | """ 56 | return self.app.remove() 57 | -------------------------------------------------------------------------------- /photoshop/api/_notifiers.py: -------------------------------------------------------------------------------- 1 | """The collection of Notifier objects in the document. 2 | 3 | Access through the Application.notifiers collection property. 4 | 5 | Examples: 6 | Notifiers must be enabled using the Application.notifiersEnabled property. 7 | ```javascript 8 | var notRef = app.notifiers.add("OnClickGoButton", eventFile) 9 | ``` 10 | 11 | """ 12 | 13 | # Import built-in modules 14 | from typing import Any 15 | from typing import Optional 16 | 17 | # Import local modules 18 | from photoshop.api._core import Photoshop 19 | from photoshop.api._notifier import Notifier 20 | 21 | 22 | class Notifiers(Photoshop): 23 | """The `notifiers` currently configured (in the Scripts Events Manager menu in the application).""" 24 | 25 | def __init__(self, parent: Optional[Any] = None): 26 | super().__init__(parent=parent) 27 | self._flag_as_method( 28 | "add", 29 | "removeAll", 30 | ) 31 | 32 | @property 33 | def _notifiers(self) -> list: 34 | return [n for n in self.app] 35 | 36 | def __len__(self): 37 | return self.length 38 | 39 | def __iter__(self): 40 | for app in self.app: 41 | yield app 42 | 43 | def __getitem__(self, item): 44 | return self._notifiers[item] 45 | 46 | @property 47 | def length(self): 48 | return len(self._notifiers) 49 | 50 | def add(self, event, event_file: Optional[Any] = None, event_class: Optional[Any] = None) -> Notifier: 51 | self.parent.notifiersEnabled = True 52 | return Notifier(self.app.add(event, event_file, event_class)) 53 | 54 | def removeAll(self): 55 | self.app.removeAll() 56 | self.parent.notifiersEnabled = False 57 | -------------------------------------------------------------------------------- /photoshop/api/_preferences.py: -------------------------------------------------------------------------------- 1 | # Import built-in modules 2 | from pathlib import Path 3 | 4 | # Import local modules 5 | from photoshop.api._core import Photoshop 6 | 7 | 8 | class Preferences(Photoshop): 9 | """The application preference settings.""" 10 | 11 | def __init__(self, parent): 12 | super().__init__(parent=parent) 13 | 14 | @property 15 | def additionalPluginFolder(self): 16 | """The path to an additional plug-in folder.""" 17 | return Path(self.app.additionalPluginFolder) 18 | 19 | @property 20 | def appendExtension(self): 21 | return self.app.appendExtension 22 | 23 | @property 24 | def askBeforeSavingLayeredTIFF(self) -> bool: 25 | return self.app.askBeforeSavingLayeredTIFF 26 | 27 | @property 28 | def autoUpdateOpenDocuments(self) -> bool: 29 | """True to automatically update open documents.""" 30 | return self.app.autoUpdateOpenDocuments 31 | 32 | @autoUpdateOpenDocuments.setter 33 | def autoUpdateOpenDocuments(self, boolean: bool): 34 | """True to automatically update open documents.""" 35 | self.app.autoUpdateOpenDocuments = boolean 36 | 37 | @property 38 | def beepWhenDone(self) -> bool: 39 | """True to beep when a process.""" 40 | return self.app.beepWhenDone 41 | 42 | @beepWhenDone.setter 43 | def beepWhenDone(self, boolean): 44 | self.app.beepWhenDone = boolean 45 | 46 | @property 47 | def colorChannelsInColor(self): 48 | """True to display component channels in the Channels palette in 49 | color.""" 50 | return self.app.colorChannelsInColor 51 | 52 | @colorChannelsInColor.setter 53 | def colorChannelsInColor(self, value): 54 | self.app.colorChannelsInColor = value 55 | 56 | @property 57 | def colorPicker(self): 58 | """The preferred color selection tool.""" 59 | return self.app.colorPicker 60 | 61 | @colorPicker.setter 62 | def colorPicker(self, value): 63 | self.app.colorPicker = value 64 | 65 | @property 66 | def columnGutter(self): 67 | return self.app.columnGutter 68 | 69 | @columnGutter.setter 70 | def columnGutter(self, value): 71 | self.app.columnGutter = value 72 | 73 | @property 74 | def columnWidth(self): 75 | return self.app.columnWidth 76 | 77 | @columnWidth.setter 78 | def columnWidth(self, value): 79 | self.app.columnWidth = value 80 | 81 | @property 82 | def createFirstSnapshot(self): 83 | """Automatically make the first snapshot when a new document is 84 | created.""" 85 | return self.app.createFirstSnapshot 86 | 87 | @createFirstSnapshot.setter 88 | def createFirstSnapshot(self, boolean): 89 | self.app.createFirstSnapshot = boolean 90 | 91 | @property 92 | def dynamicColorSliders(self): 93 | return self.app.dynamicColorSliders 94 | 95 | @dynamicColorSliders.setter 96 | def dynamicColorSliders(self, boolean): 97 | self.app.dynamicColorSliders = boolean 98 | 99 | @property 100 | def editLogItems(self) -> bool: 101 | """The preferred level of detail in the history log.""" 102 | return self.app.editLogItems 103 | 104 | @editLogItems.setter 105 | def editLogItems(self, boolean: bool): 106 | """The preferred level of detail in the history log. 107 | 108 | Valid only when useHistoryLog = True 109 | 110 | """ 111 | self.app.editLogItems = boolean 112 | 113 | @property 114 | def exportClipboard(self): 115 | """Retain Photoshop contents on the clipboard after exit the app.""" 116 | return self.app.exportClipboard 117 | 118 | @exportClipboard.setter 119 | def exportClipboard(self, boolean: bool): 120 | self.app.exportClipboard = boolean 121 | 122 | @property 123 | def fontPreviewSize(self): 124 | return self.app.fontPreviewSize 125 | 126 | @fontPreviewSize.setter 127 | def fontPreviewSize(self, value): 128 | self.app.fontPreviewSize = value 129 | 130 | @property 131 | def fullSizePreview(self): 132 | return self.app.fullSizePreview 133 | 134 | @fullSizePreview.setter 135 | def fullSizePreview(self, value): 136 | self.app.fullSizePreview = value 137 | 138 | @property 139 | def gamutWarningOpacity(self): 140 | return self.app.gamutWarningOpacity 141 | 142 | @property 143 | def rulerUnits(self): 144 | return self.app.rulerUnits 145 | 146 | @rulerUnits.setter 147 | def rulerUnits(self, value): 148 | self.app.rulerUnits = value 149 | -------------------------------------------------------------------------------- /photoshop/api/_selection.py: -------------------------------------------------------------------------------- 1 | """The selected area of the document or layer.""" 2 | 3 | # Import local modules 4 | from photoshop.api._core import Photoshop 5 | from photoshop.api.enumerations import ColorBlendMode 6 | from photoshop.api.enumerations import SelectionType 7 | from photoshop.api.solid_color import SolidColor 8 | 9 | 10 | # pylint: disable=too-many-public-methods, too-many-arguments 11 | class Selection(Photoshop): 12 | """The selected area of the document.""" 13 | 14 | def __init__(self, parent=None): 15 | super().__init__(parent=parent) 16 | self._flag_as_method( 17 | "clear", 18 | "contract", 19 | "copy", 20 | "cut", 21 | "deselect", 22 | "expand", 23 | "feather", 24 | "fill", 25 | "grow", 26 | "invert", 27 | "load", 28 | "makeWorkPath", 29 | "resize", 30 | "resizeBoundary", 31 | "rotate", 32 | "rotateBoundary", 33 | "select", 34 | "selectBorder", 35 | "similar", 36 | "smooth", 37 | "store", 38 | "stroke", 39 | "translate", 40 | "translateBoundary", 41 | ) 42 | 43 | @property 44 | def bounds(self): 45 | return self.app.bounds 46 | 47 | def parent(self): 48 | return self.app.parent 49 | 50 | @property 51 | def solid(self): 52 | return self.app.solid 53 | 54 | @property 55 | def typename(self): 56 | return self.app.typename 57 | 58 | def clear(self): 59 | """Clears the selection and does not copy it to the clipboard.""" 60 | self.app.clear() 61 | 62 | def contract(self, contract_by): 63 | """Contracts the selection.""" 64 | self.app.contract(contract_by) 65 | 66 | def copy(self): 67 | """Copies the selection to the clipboard.""" 68 | self.app.copy() 69 | 70 | def cut(self): 71 | """Cuts the current selection to the clipboard.""" 72 | self.app.cut() 73 | 74 | def select(self, *args, **kwargs): 75 | return self.app.select(*args, **kwargs) 76 | 77 | def deselect(self): 78 | """Deselects the current selection.""" 79 | return self.app.deselect() 80 | 81 | def expand(self, by: int): 82 | """Expands the selection. 83 | 84 | Args: 85 | by: The amount to expand the selection. 86 | 87 | """ 88 | self.app.expand(by) 89 | 90 | def feather(self, by: int): 91 | """Feathers the edges of the selection. 92 | 93 | Args: 94 | by: The amount to feather the edge. 95 | 96 | """ 97 | return self.app.feather(by) 98 | 99 | def fill( 100 | self, 101 | fill_type: SolidColor, 102 | mode: ColorBlendMode = None, 103 | opacity=None, 104 | preserve_transparency=None, 105 | ): 106 | """Fills the selection.""" 107 | return self.app.fill(fill_type, mode, opacity, preserve_transparency) 108 | 109 | def grow(self, tolerance, anti_alias): 110 | """Grows the selection to include all adjacent pixels falling within 111 | 112 | The specified tolerance range. 113 | 114 | Args: 115 | tolerance (int): The tolerance range. Range: 0 to 255. 116 | anti_alias (bool): If true, anti-aliasing is used. 117 | 118 | 119 | """ 120 | return self.app.grow(tolerance, anti_alias) 121 | 122 | def invert(self): 123 | """Inverts the selection.""" 124 | self.app.invert() 125 | 126 | def load(self, from_channel, combination, inverting): 127 | """Loads the selection from the specified channel.""" 128 | return self.app.load(from_channel, combination, inverting) 129 | 130 | def makeWorkPath(self, tolerance): 131 | """Makes this selection item the workpath for this document.""" 132 | self.app.makeWorkPath(tolerance) 133 | 134 | def resize(self, horizontal, vertical, anchor): 135 | """Resizes the selected area to the specified dimensions and anchor 136 | position.""" 137 | self.app.resize(horizontal, vertical, anchor) 138 | 139 | def resizeBoundary(self, horizontal, vertical, anchor): 140 | """Scales the boundary of the selection.""" 141 | self.app.resizeBoundary(horizontal, vertical, anchor) 142 | 143 | def rotate(self, angle, anchor): 144 | """Rotates the object.""" 145 | self.app.rotate(angle, anchor) 146 | 147 | def rotateBoundary(self, angle, anchor): 148 | """Rotates the boundary of the selection.""" 149 | self.app.rotateBoundary(angle, anchor) 150 | 151 | def stroke(self, strokeColor, width, location, mode, opacity, preserveTransparency): 152 | """Strokes the selection. 153 | 154 | Args: 155 | strokeColor (SolidColor): The color to stroke the selection with. 156 | width (int): The stroke width. 157 | location (int): The stroke location. 158 | mode (int): The color blend mode. 159 | opacity (int): The opacity of the stroke color as a percentage. 160 | Range: 1 to 100. 161 | preserveTransparency (bool): If true, preserves transparency. 162 | 163 | """ 164 | return self.app.stroke(strokeColor, width, location, mode, opacity, preserveTransparency) 165 | 166 | def selectBorder(self, width): 167 | """Selects the selection border only (in the specified width); 168 | subsequent actions do not affect the selected area within the borders. 169 | 170 | Args: 171 | width (int): The width of the border selection. 172 | 173 | """ 174 | return self.app.selectBorder(width) 175 | 176 | def similar(self, tolerance, antiAlias): 177 | return self.app.similar(tolerance, antiAlias) 178 | 179 | def smooth(self, radius): 180 | """Cleans up stray pixels left inside or outside a color-based 181 | selection (within the radius specified in pixels).""" 182 | return self.app.smooth(radius) 183 | 184 | def store(self, into, combination=SelectionType.ReplaceSelection): 185 | """Saves the selection as a channel.""" 186 | return self.app.store(into, combination) 187 | 188 | def translate(self, deltaX, deltaY): 189 | """Moves the object relative to its current position.""" 190 | return self.app.translate(deltaX, deltaY) 191 | 192 | def translateBoundary(self, deltaX, deltaY): 193 | """Moves the boundary of selection relative to its current position.""" 194 | return self.app.translateBoundary(deltaX, deltaY) 195 | -------------------------------------------------------------------------------- /photoshop/api/_text_fonts.py: -------------------------------------------------------------------------------- 1 | # Import built-in modules 2 | from typing import Any 3 | from typing import Union 4 | 5 | # Import third-party modules 6 | from comtypes import ArgumentError 7 | from comtypes import COMError 8 | 9 | # Import local modules 10 | from photoshop.api._core import Photoshop 11 | from photoshop.api.errors import PhotoshopPythonAPIError 12 | from photoshop.api.text_font import TextFont 13 | 14 | 15 | class TextFonts(Photoshop): 16 | """An installed font.""" 17 | 18 | def __init__(self, parent=None): 19 | super().__init__(parent=parent) 20 | 21 | """ 22 | MAGIC METHODS 23 | """ 24 | 25 | def __len__(self): 26 | return self.length 27 | 28 | def __iter__(self): 29 | for font in self.app: 30 | yield TextFont(font) 31 | 32 | def __contains__(self, name: str): 33 | """Check if a font is installed. Lookup by font postScriptName (fastest) or name. 34 | 35 | Args: 36 | name: Name or postScriptName of the font to look for. 37 | 38 | Returns: 39 | bool: True if font is found, otherwise False. 40 | """ 41 | # Look for postScriptName 42 | if self.get(name): 43 | return True 44 | # Look for name (slow) 45 | for font in self: 46 | try: 47 | if font.name == name: 48 | return True 49 | except COMError: 50 | continue 51 | return False 52 | 53 | def __getitem__(self, key: str): 54 | """Access a given TextFont using dictionary key lookup, must provide the postScriptName. 55 | 56 | Args: 57 | key: The postScriptName of the font. 58 | 59 | Returns: 60 | TextFont instance. 61 | 62 | """ 63 | try: 64 | return TextFont(self.app[key]) 65 | except ArgumentError: 66 | raise PhotoshopPythonAPIError(f'Could not find a font with postScriptName "{key}"') 67 | 68 | """ 69 | METHODS 70 | """ 71 | 72 | def get(self, key: str, default: Any = None) -> Union[TextFont, Any]: 73 | """ 74 | Accesses a given TextFont using dictionary key lookup of postScriptName, returns default if not found. 75 | 76 | Args: 77 | key: The postScriptName of the font. 78 | default: Value to return if font isn't found. 79 | 80 | Returns: 81 | TextFont instance. 82 | 83 | """ 84 | try: 85 | return TextFont(self.app[key]) 86 | except (KeyError, ArgumentError): 87 | return default 88 | 89 | def getByName(self, name: str) -> TextFont: 90 | """Gets the font by the font name. 91 | 92 | Args: 93 | name: The name of the font. 94 | 95 | 96 | Returns: 97 | font instance. 98 | 99 | """ 100 | for font in self.app: 101 | if font.name == name: 102 | return TextFont(font) 103 | raise PhotoshopPythonAPIError('Could not find a TextFont named "{name}"') 104 | 105 | """ 106 | PROPERTIES 107 | """ 108 | 109 | @property 110 | def _fonts(self): 111 | return [a for a in self.app] 112 | 113 | @property 114 | def length(self): 115 | """The number pf elements in the collection.""" 116 | return len(self._fonts) 117 | -------------------------------------------------------------------------------- /photoshop/api/action_list.py: -------------------------------------------------------------------------------- 1 | """This object provides an array-style mechanism for storing data. 2 | 3 | It can be used for low-level access info Photoshop. 4 | 5 | 6 | """ 7 | # Import local modules 8 | from photoshop.api._core import Photoshop 9 | 10 | 11 | class ActionList(Photoshop): 12 | """The list of commands that comprise an Action. 13 | 14 | (such as an Action created using the Actions palette in the Adobe Photoshop application). 15 | The action list object is part of the Action Manager functionality. 16 | For details on using the Action Manager, see the Photoshop Scripting Guide. 17 | 18 | """ 19 | 20 | object_name = "ActionList" 21 | 22 | def __init__(self, parent=None): 23 | super().__init__(parent=parent) 24 | self._flag_as_method( 25 | "getBoolean", 26 | "getClass", 27 | "getData", 28 | "getDouble", 29 | "getEnumerationType", 30 | "getEnumerationValue", 31 | "getInteger", 32 | "getLargeInteger", 33 | "getList", 34 | "getObjectType", 35 | ) 36 | 37 | @property 38 | def count(self): 39 | return self.app.count 40 | 41 | def getBoolean(self, index): 42 | return self.app.getBoolean(index) 43 | 44 | def getClass(self, index): 45 | return self.app.getClass(index) 46 | 47 | def getData(self, index): 48 | return self.app.getData(index) 49 | 50 | def getDouble(self, index): 51 | return self.app.getDouble(index) 52 | 53 | def getEnumerationType(self, index): 54 | return self.app.getEnumerationType(index) 55 | 56 | def getEnumerationValue(self, index): 57 | return self.app.getEnumerationValue(index) 58 | 59 | def getInteger(self, index): 60 | return self.app.getInteger(index) 61 | 62 | def getLargeInteger(self, index): 63 | return self.app.getLargeInteger(index) 64 | 65 | def getList(self, index): 66 | return self.app.getList(index) 67 | 68 | def getObjectType(self, index): 69 | return self.app.getObjectType(index) 70 | -------------------------------------------------------------------------------- /photoshop/api/action_reference.py: -------------------------------------------------------------------------------- 1 | """This object provides information about what the action is refering to. 2 | 3 | For example, when referring to the name of something you might use keyName. 4 | The reference would also need to know what name you are referring to. 5 | In this case you could use classDocument for the name of the document or 6 | classLayer for the name of the layer. 7 | It can be used for low-level access into Contains data associated 8 | with an ActionDescriptor. 9 | 10 | """ 11 | # Import local modules 12 | from photoshop.api._core import Photoshop 13 | from photoshop.api.enumerations import ReferenceFormType 14 | 15 | 16 | class ActionReference(Photoshop): 17 | """Contains data describing a referenced Action. 18 | 19 | The action reference object is part of the Action Manager functionality. 20 | For details on using the Action Manager, see the Photoshop Scripting Guide. 21 | 22 | """ 23 | 24 | object_name = "ActionReference" 25 | 26 | def __init__(self, parent=None): 27 | super().__init__(parent=parent) 28 | self._flag_as_method( 29 | "getContainer", 30 | "getDesiredClass", 31 | "getEnumeratedType", 32 | "getEnumeratedValue", 33 | "getForm", 34 | "getIdentifier", 35 | "getIndex", 36 | "putName", 37 | "putClass", 38 | "putEnumerated", 39 | "putIdentifier", 40 | "putIndex", 41 | "putOffset", 42 | "putProperty", 43 | ) 44 | 45 | def getContainer(self): 46 | return self.app.getContainer() 47 | 48 | def getDesiredClass(self): 49 | return self.app.getDesiredClass() 50 | 51 | def getEnumeratedType(self) -> int: 52 | return self.app.getEnumeratedType() 53 | 54 | def getEnumeratedValue(self) -> int: 55 | return self.app.getEnumeratedValue() 56 | 57 | def getForm(self) -> ReferenceFormType: 58 | """Gets the form of this action reference.""" 59 | return ReferenceFormType(self.app.getForm()) 60 | 61 | def getIdentifier(self) -> int: 62 | """Gets the identifier value for a reference whose form is 63 | identifier.""" 64 | return self.app.getIdentifier() 65 | 66 | def getIndex(self) -> int: 67 | """Gets the index value for a reference in a list or array,""" 68 | return self.app.getIndex() 69 | 70 | def putName(self, key, value): 71 | return self.app.putName(key, value) 72 | 73 | def putClass(self, value): 74 | return self.app.putClass(value) 75 | 76 | def putEnumerated(self, desired_class, enum_type, value): 77 | """Puts an enumeration type and ID into a reference along with the 78 | desired class for the reference.""" 79 | return self.app.putEnumerated(desired_class, enum_type, value) 80 | 81 | def putIdentifier(self, desired_class, value): 82 | return self.app.putIdentifier(desired_class, value) 83 | 84 | def putIndex(self, desired_class, value): 85 | return self.app.putIndex(desired_class, value) 86 | 87 | def putOffset(self, desired_class, value): 88 | return self.app.putOffset(desired_class, value) 89 | 90 | def putProperty(self, desired_class, value): 91 | return self.app.putProperty(desired_class, value) 92 | -------------------------------------------------------------------------------- /photoshop/api/batch_options.py: -------------------------------------------------------------------------------- 1 | # https://theiviaxx.github.io/photoshop-docs/Photoshop/BatchOptions.html 2 | # Import local modules 3 | from photoshop.api._core import Photoshop 4 | 5 | 6 | class BatchOptions(Photoshop): 7 | object_name = "BatchOptions" 8 | 9 | def __init__(self): 10 | super().__init__() 11 | 12 | @property 13 | def destination(self): 14 | """The type of destination for the processed files.""" 15 | return self.app.destination 16 | 17 | @destination.setter 18 | def destination(self, value): 19 | self.app.destination = value 20 | 21 | @property 22 | def destinationFolder(self): 23 | """The folder location for the processed files. Valid only when ‘destination’ = folder.""" 24 | return self.app.destinationFolder 25 | 26 | @destinationFolder.setter 27 | def destinationFolder(self, path): 28 | self.app.destinationFolder = path 29 | 30 | @property 31 | def errorFile(self): 32 | """The file in which to log errors encountered. 33 | To display errors on the screen and stop batch processing when errors occur, leave blank.""" 34 | return self.app.errorFile 35 | 36 | @errorFile.setter 37 | def errorFile(self, file_path): 38 | self.app.errorFile = file_path 39 | 40 | @property 41 | def fileNaming(self) -> list: 42 | """A list of file naming options. Maximum: 6.""" 43 | return self.app.fileNaming 44 | 45 | @fileNaming.setter 46 | def fileNaming(self, file_naming: list): 47 | self.app.fileNaming = file_naming 48 | 49 | @property 50 | def macintoshCompatible(self) -> bool: 51 | """If true, the final file names are Macintosh compatible.""" 52 | return self.app.macintoshCompatible 53 | 54 | @macintoshCompatible.setter 55 | def macintoshCompatible(self, value: bool): 56 | self.app.macintoshCompatible = value 57 | 58 | @property 59 | def overrideOpen(self) -> bool: 60 | """If true, overrides action open commands.""" 61 | return self.app.overrideOpen 62 | 63 | @overrideOpen.setter 64 | def overrideOpen(self, value: bool): 65 | self.app.overrideOpen = value 66 | 67 | @property 68 | def overrideSave(self) -> bool: 69 | """If true, overrides save as action steps with the specified destination.""" 70 | return self.app.overrideSave 71 | 72 | @overrideSave.setter 73 | def overrideSave(self, value: bool): 74 | self.app.overrideSave = value 75 | 76 | @property 77 | def startingSerial(self) -> int: 78 | """The starting serial number to use in naming files.""" 79 | return self.app.startingSerial 80 | 81 | @startingSerial.setter 82 | def startingSerial(self, value: int): 83 | self.app.startingSerial = value 84 | 85 | @property 86 | def suppressOpen(self) -> bool: 87 | """If true, suppresses file open options dialogs.""" 88 | return self.app.suppressOpen 89 | 90 | @suppressOpen.setter 91 | def suppressOpen(self, value: bool): 92 | self.app.suppressOpen = value 93 | 94 | @property 95 | def suppressProfile(self) -> bool: 96 | """If true, suppresses color profile warnings.""" 97 | return self.app.suppressProfile 98 | 99 | @suppressProfile.setter 100 | def suppressProfile(self, value: bool): 101 | self.app.suppressProfile = value 102 | 103 | @property 104 | def unixCompatible(self) -> bool: 105 | """If true, the final file names are Unix compatible.""" 106 | return self.app.unixCompatible 107 | 108 | @unixCompatible.setter 109 | def unixCompatible(self, value: bool): 110 | self.app.unixCompatible = value 111 | 112 | @property 113 | def windowsCompatible(self) -> bool: 114 | """If true, the final file names are Windows compatible.""" 115 | return self.app.windowsCompatible 116 | 117 | @windowsCompatible.setter 118 | def windowsCompatible(self, value: bool): 119 | self.app.windowsCompatible = value 120 | -------------------------------------------------------------------------------- /photoshop/api/colors/__init__.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api.colors.cmyk import CMYKColor 3 | from photoshop.api.colors.gray import GrayColor 4 | from photoshop.api.colors.hsb import HSBColor 5 | from photoshop.api.colors.lab import LabColor 6 | from photoshop.api.colors.rgb import RGBColor 7 | 8 | 9 | __all__ = [ 10 | CMYKColor.__name__, 11 | GrayColor.__name__, 12 | HSBColor.__name__, 13 | LabColor.__name__, 14 | RGBColor.__name__, 15 | ] 16 | -------------------------------------------------------------------------------- /photoshop/api/colors/cmyk.py: -------------------------------------------------------------------------------- 1 | """Defines a CMYK color, used in the `SolidColor` object.""" 2 | 3 | # Import local modules 4 | from photoshop.api._core import Photoshop 5 | 6 | 7 | class CMYKColor(Photoshop): 8 | """A CMYK color specification.""" 9 | 10 | object_name = "CMYKColor" 11 | 12 | def __init__(self, parent): 13 | super().__init__(parent=parent) 14 | 15 | @property 16 | def black(self) -> int: 17 | """The black color value. Range: 0.0 to 100.0.""" 18 | return round(self.app.black) 19 | 20 | @black.setter 21 | def black(self, value: int): 22 | self.app.black = value 23 | 24 | @property 25 | def cyan(self) -> int: 26 | """The cyan color value. Range: 0.0 to 100.0.""" 27 | return round(self.app.cyan) 28 | 29 | @cyan.setter 30 | def cyan(self, value: int): 31 | self.app.cyan = value 32 | 33 | @property 34 | def magenta(self) -> int: 35 | """The magenta color value. Range: 0.0 to 100.0.""" 36 | return round(self.app.magenta) 37 | 38 | @magenta.setter 39 | def magenta(self, value: int): 40 | self.app.magenta = value 41 | 42 | @property 43 | def yellow(self) -> int: 44 | """The yellow color value. Range: 0.0 to 100.0.""" 45 | return round(self.app.yellow) 46 | 47 | @yellow.setter 48 | def yellow(self, value: int): 49 | self.app.yellow = value 50 | -------------------------------------------------------------------------------- /photoshop/api/colors/gray.py: -------------------------------------------------------------------------------- 1 | """Defines a gray color, used in the `SolidColor` object.""" 2 | 3 | # Import local modules 4 | from photoshop.api._core import Photoshop 5 | 6 | 7 | class GrayColor(Photoshop): 8 | """Options for defining a gray color.""" 9 | 10 | object_name = "GrayColor" 11 | 12 | def __init__(self, parent): 13 | super().__init__(parent=parent) 14 | 15 | @property 16 | def gray(self) -> float: 17 | """The gray value.""" 18 | return self.app.gray 19 | 20 | @gray.setter 21 | def gray(self, value: float): 22 | """The gray value.""" 23 | self.app.gray = value 24 | -------------------------------------------------------------------------------- /photoshop/api/colors/hsb.py: -------------------------------------------------------------------------------- 1 | """Defines an HSB color, used in the `SolidColor` object.""" 2 | 3 | # Import local modules 4 | from photoshop.api._core import Photoshop 5 | 6 | 7 | class HSBColor(Photoshop): 8 | """An HSB color specification.""" 9 | 10 | object_name = "HSBColor" 11 | 12 | def __init__(self, parent): 13 | super().__init__(parent=parent) 14 | 15 | @property 16 | def brightness(self): 17 | return round(self.app.brightness) 18 | 19 | @brightness.setter 20 | def brightness(self, value): 21 | self.app.brightness = value 22 | 23 | @property 24 | def saturation(self): 25 | return round(self.app.saturation) 26 | 27 | @saturation.setter 28 | def saturation(self, value): 29 | self.app.saturation = value 30 | 31 | @property 32 | def hue(self): 33 | """The hue value. Range: 0.0 to 360.0.""" 34 | return round(self.app.hue) 35 | 36 | @hue.setter 37 | def hue(self, value): 38 | self.app.hue = value 39 | -------------------------------------------------------------------------------- /photoshop/api/colors/lab.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class LabColor(Photoshop): 6 | """A Lab color specification.""" 7 | 8 | object_name = "LabColor" 9 | 10 | def __init__(self, parent): 11 | super().__init__(parent=parent) 12 | 13 | @property 14 | def A(self): 15 | return round(self.app.A) 16 | 17 | @A.setter 18 | def A(self, value): 19 | self.app.A = value 20 | 21 | @property 22 | def B(self): 23 | return round(self.app.B) 24 | 25 | @B.setter 26 | def B(self, value): 27 | self.app.B = value 28 | 29 | @property 30 | def L(self): 31 | return round(self.app.L) 32 | 33 | @L.setter 34 | def L(self, value): 35 | self.app.L = value 36 | -------------------------------------------------------------------------------- /photoshop/api/colors/rgb.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class RGBColor(Photoshop): 6 | """The definition of an RGB color mode.""" 7 | 8 | object_name = "RGBColor" 9 | 10 | def __init__(self, parent): 11 | super().__init__(parent=parent) 12 | self.blue = self.app.blue 13 | self.green = self.app.green 14 | self.red = self.app.red 15 | 16 | @property 17 | def blue(self) -> int: 18 | return round(self.app.blue) 19 | 20 | @blue.setter 21 | def blue(self, value: int): 22 | self.app.blue = value 23 | 24 | @property 25 | def green(self) -> int: 26 | return round(self.app.green) 27 | 28 | @green.setter 29 | def green(self, value: int): 30 | self.app.green = value 31 | 32 | @property 33 | def red(self) -> int: 34 | return round(self.app.red) 35 | 36 | @red.setter 37 | def red(self, value: int): 38 | self.app.red = value 39 | 40 | @property 41 | def hexValue(self): 42 | return self.app.hexValue 43 | 44 | @hexValue.setter 45 | def hexValue(self, value): 46 | self.app.hexValue = value 47 | 48 | def __str__(self): 49 | return f"[red: {self.red}, green:{self.green}, blue:{self.blue})]" 50 | -------------------------------------------------------------------------------- /photoshop/api/constants.py: -------------------------------------------------------------------------------- 1 | # The Photoshop version to COM program ID mappings. 2 | PHOTOSHOP_VERSION_MAPPINGS = { 3 | "2025": "190", 4 | "2024": "180", 5 | "2023": "170", 6 | "2022": "160", 7 | "2021": "150", 8 | "2020": "140", 9 | "2019": "130", 10 | "2018": "120", 11 | "2017": "110", 12 | } 13 | -------------------------------------------------------------------------------- /photoshop/api/errors.py: -------------------------------------------------------------------------------- 1 | # Import third-party modules 2 | from comtypes import COMError 3 | 4 | 5 | class PhotoshopPythonAPIError(Exception): 6 | pass 7 | 8 | 9 | class PhotoshopPythonAPICOMError(COMError): 10 | pass 11 | 12 | 13 | __all__ = ["PhotoshopPythonAPIError", "PhotoshopPythonAPICOMError"] 14 | -------------------------------------------------------------------------------- /photoshop/api/event_id.py: -------------------------------------------------------------------------------- 1 | # Import built-in modules 2 | from enum import Enum 3 | 4 | 5 | class EventID(str, Enum): 6 | """All event ids.""" 7 | 8 | # Here is a list of JSON CallBack events in Photoshop. 9 | # https://community.adobe.com/t5/get-started/photoshop-json-callback-events-list-up-to-cc2015-ver-16/td-p/4792115?page=1 10 | TDTransform = "TdT " 11 | Average = "Avrg" 12 | ApplyStyle = "ASty" 13 | Assert = "Asrt" 14 | AccentedEdges = "AccE" 15 | Add = "Add" 16 | AddNoise = "AdNs" 17 | AddTo = "AddT" 18 | Align = "Algn" 19 | All = "All " 20 | AngledStrokes = "AngS" 21 | ApplyImage = "AppI" 22 | BasRelief = "BsRl" 23 | Batch = "Btch" 24 | BatchFromDroplet = "BtcF" 25 | Blur = "Blr " 26 | BlurMore = "BlrM" 27 | Border = "Brdr" 28 | Brightness = "BrgC" 29 | CanvasSize = "CnvS" 30 | ChalkCharcoal = "ChlC" 31 | ChannelMixer = "ChnM" 32 | Charcoal = "Chrc" 33 | Chrome = "Chrm" 34 | Clear = "Cler" 35 | Close = "Cls " 36 | Clouds = "Clds" 37 | ColorBalance = "ClrB" 38 | ColorHalftone = "ClrH" 39 | ColorRange = "ClrR" 40 | ColoredPencil = "ClrP" 41 | ContactSheet = "0B71D221-F8CE-11d2-B21B-0008C75B322C" 42 | ConteCrayon = "CntC" 43 | Contract = "Cntc" 44 | ConvertMode = "CnvM" 45 | Copy = "copy" 46 | CopyEffects = "CpFX" 47 | CopyMerged = "CpyM" 48 | CopyToLayer = "CpTL" 49 | Craquelure = "Crql" 50 | CreateDroplet = "CrtD" 51 | Crop = "Crop" 52 | Crosshatch = "Crsh" 53 | Crystallize = "Crst" 54 | Curves = "Crvs" 55 | Custom = "Cstm" 56 | Cut = "cut " 57 | CutToLayer = "CtTL" 58 | Cutout = "Ct " 59 | DarkStrokes = "DrkS" 60 | DeInterlace = "Dntr" 61 | DefinePattern = "DfnP" 62 | Defringe = "Dfrg" 63 | Delete = "Dlt " 64 | Desaturate = "Dstt" 65 | Deselect = "Dslc" 66 | Despeckle = "Dspc" 67 | DifferenceClouds = "DrfC" 68 | Diffuse = "Dfs " 69 | DiffuseGlow = "DfsG" 70 | DisableLayerFX = "dlfx" 71 | Displace = "Dspl" 72 | Distribute = "Dstr" 73 | Draw = "Draw" 74 | DryBrush = "DryB" 75 | Duplicate = "Dplc" 76 | DustAndScratches = "DstS" 77 | Emboss = "Embs" 78 | Equalize = "Eqlz" 79 | Exchange = "Exch" 80 | Expand = "Expn" 81 | Export = "Expr" 82 | Jumpto = "Jpto" 83 | ExportTransparentImage = "02879e00-cb66-11d1-bc43-0060b0a13dc4" 84 | Extrude = "Extr" 85 | Facet = "Fct " 86 | Fade = "Fade" 87 | Feather = "Fthr" 88 | Fibers = "Fbrs" 89 | Fill = "Fl " 90 | FilmGrain = "FlmG" 91 | Filter = "Fltr" 92 | FindEdges = "FndE" 93 | FitImage = "3caa3434-cb67-11d1-bc43-0060b0a13dc4" 94 | FlattenImage = "FltI" 95 | Flip = "Flip" 96 | Fragment = "Frgm" 97 | Fresco = "Frsc" 98 | GaussianBlur = "GsnB" 99 | Get = "getd" 100 | Glass = "Gls " 101 | GlowingEdges = "GlwE" 102 | Gradient = "Grdn" 103 | GradientMap = "GrMp" 104 | Grain = "Grn " 105 | GraphicPen = "GraP" 106 | Group = "GrpL" 107 | Grow = "Grow" 108 | HalftoneScreen = "HlfS" 109 | Hide = "Hd " 110 | HighPass = "HghP" 111 | HSBHSL = "HsbP" 112 | HueSaturation = "HStr" 113 | ImageSize = "ImgS" 114 | Import = "Impr" 115 | InkOutlines = "InkO" 116 | Intersect = "Intr" 117 | IntersectWith = "IntW" 118 | Inverse = "Invs" 119 | Invert = "Invr" 120 | LensFlare = "LnsF" 121 | Levels = "Lvls" 122 | LightingEffects = "LghE" 123 | Link = "Lnk " 124 | Make = "Mk " 125 | Maximum = "Mxm " 126 | Median = "Mdn " 127 | MergeLayers = "Mrg2" 128 | MergeLayersOld = "MrgL" 129 | MergeSpotChannel = "MSpt" 130 | MergeVisible = "MrgV" 131 | Mezzotint = "Mztn" 132 | Minimum = "Mnm " 133 | ModeChange = "8cba8cd6-cb66-11d1-bc43-0060b0a13dc4" 134 | Mosaic = "Msc " 135 | Mosaic_PLUGIN = "MscT" 136 | MotionBlur = "MtnB" 137 | Move = "move" 138 | NTSCColors = "NTSC" 139 | NeonGlow = "NGlw" 140 | Next = "Nxt " 141 | NotePaper = "NtPr" 142 | Notify = "Ntfy" 143 | Null = "typeNull" 144 | OceanRipple = "OcnR" 145 | Offset = "Ofst" 146 | Open = "Opn " 147 | Paint = "Pnt " 148 | PaintDaubs = "PntD" 149 | PaletteKnife = "PltK" 150 | Paste = "past" 151 | PasteEffects = "PaFX" 152 | PasteInto = "PstI" 153 | PasteOutside = "PstO" 154 | Patchwork = "Ptch" 155 | Photocopy = "Phtc" 156 | PicturePackage = "4C1ABF40-DD82-11d2-B20F-0008C75B322C" 157 | Pinch = "Pnch" 158 | Place = "Plc " 159 | Plaster = "Plst" 160 | PlasticWrap = "PlsW" 161 | Play = "Ply " 162 | Pointillize = "Pntl" 163 | Polar = "Plr " 164 | PosterEdges = "PstE" 165 | Posterize = "Pstr" 166 | Previous = "Prvs" 167 | Print = "Prnt" 168 | ProfileToProfile = "PrfT" 169 | Purge = "Prge" 170 | Quit = "quit" 171 | RadialBlur = "RdlB" 172 | Rasterize = "Rstr" 173 | RasterizeTypeSheet = "RstT" 174 | RemoveBlackMatte = "RmvB" 175 | RemoveLayerMask = "RmvL" 176 | RemoveWhiteMatte = "RmvW" 177 | Rename = "Rnm " 178 | ReplaceColor = "RplC" 179 | Reset = "Rset" 180 | ResizeImage = "1333cf0c-cb67-11d1-bc43-0060b0a13dc4" 181 | Reticulation = "Rtcl" 182 | Revert = "Rvrt" 183 | Ripple = "Rple" 184 | Rotate = "Rtte" 185 | RoughPastels = "RghP" 186 | Save = "save" 187 | Select = "slct" 188 | SelectiveColor = "SlcC" 189 | Set = "setd" 190 | SharpenEdges = "ShrE" 191 | Sharpen = "Shrp" 192 | SharpenMore = "ShrM" 193 | Shear = "Shr " 194 | Show = "Shw " 195 | Similar = "Smlr" 196 | SmartBlur = "SmrB" 197 | Smooth = "Smth" 198 | SmudgeStick = "SmdS" 199 | Solarize = "Slrz" 200 | Spatter = "Spt " 201 | Spherize = "Sphr" 202 | SplitChannels = "SplC" 203 | Sponge = "Spng" 204 | SprayedStrokes = "SprS" 205 | StainedGlass = "StnG" 206 | Stamp = "Stmp" 207 | Stop = "Stop" 208 | Stroke = "Strk" 209 | Subtract = "Sbtr" 210 | SubtractFrom = "SbtF" 211 | Sumie = "Smie" 212 | TakeMergedSnapshot = "TkMr" 213 | TakeSnapshot = "TkSn" 214 | TextureFill = "TxtF" 215 | Texturizer = "Txtz" 216 | Threshold = "Thrs" 217 | Tiles = "Tls " 218 | TornEdges = "TrnE" 219 | TraceContour = "TrcC" 220 | Transform = "Trnf" 221 | Trap = "Trap" 222 | Twirl = "Twrl" 223 | Underpainting = "Undr" 224 | Undo = "undo" 225 | Ungroup = "Ungr" 226 | Unlink = "Unlk" 227 | UnsharpMask = "UnsM" 228 | Variations = "Vrtn" 229 | Wait = "Wait" 230 | WaterPaper = "WtrP" 231 | Watercolor = "Wtrc" 232 | Wave = "Wave" 233 | Wind = "Wnd " 234 | ZigZag = "ZgZg" 235 | BackLight = "BacL" 236 | FillFlash = "FilE" 237 | ColorCast = "ColE" 238 | OpenUntitled = "OpnU" 239 | PresetKind = "presetKindType" 240 | SmartSharpen = "smartSharpen" 241 | PresetKindType = "presetKindType" 242 | PresetKindCustom = "presetKindCustom" 243 | NoiseReduction = "noiseReduction" 244 | BlurType = "blurType" 245 | ContentLayer = "contentLayer" 246 | SaveStage = "saveStage" 247 | SaveStageType = "saveStageType" 248 | SaveSucceeded = "saveSucceeded" 249 | RasterizeLayer = "rasterizeLayer" 250 | ForceNotify = "forceNotify" 251 | PlacedLayerEditContents = "placedLayerEditContents" 252 | -------------------------------------------------------------------------------- /photoshop/api/open_options/__init__.py: -------------------------------------------------------------------------------- 1 | from ..open_options.eps import EPSOpenOptions 2 | 3 | 4 | __all__ = [ 5 | EPSOpenOptions.__name__, 6 | ] 7 | -------------------------------------------------------------------------------- /photoshop/api/open_options/eps.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class EPSOpenOptions(Photoshop): 6 | """Options for saving a document in EPS format. 7 | 8 | using the `Document.saveAs()` 9 | 10 | """ 11 | 12 | object_name = "EPSOpenOptions" 13 | 14 | def __init__(self): 15 | super().__init__() 16 | 17 | @property 18 | def antiAlias(self): 19 | return self.app.antiAlias 20 | 21 | @property 22 | def constrainProportions(self): 23 | return self.app.constrainProportions 24 | 25 | @property 26 | def height(self): 27 | return self.app.height 28 | 29 | @property 30 | def mode(self): 31 | return self.app.mode 32 | 33 | @property 34 | def resolution(self): 35 | return self.app.resolution 36 | 37 | @property 38 | def width(self): 39 | return self.app.width 40 | 41 | @property 42 | def embedColorProfile(self): 43 | return self.app.embedColorProfile 44 | 45 | @embedColorProfile.setter 46 | def embedColorProfile(self, boolean): 47 | self.app.embedColorProfile = boolean 48 | -------------------------------------------------------------------------------- /photoshop/api/save_options/__init__.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api.save_options.bmp import BMPSaveOptions 3 | from photoshop.api.save_options.eps import EPSSaveOptions 4 | from photoshop.api.save_options.gif import GIFSaveOptions 5 | from photoshop.api.save_options.jpg import JPEGSaveOptions 6 | from photoshop.api.save_options.pdf import PDFSaveOptions 7 | from photoshop.api.save_options.png import ExportOptionsSaveForWeb 8 | from photoshop.api.save_options.png import PNGSaveOptions 9 | from photoshop.api.save_options.psd import PhotoshopSaveOptions 10 | from photoshop.api.save_options.tag import TargaSaveOptions 11 | from photoshop.api.save_options.tif import TiffSaveOptions 12 | 13 | 14 | __all__ = [ 15 | BMPSaveOptions.__name__, 16 | EPSSaveOptions.__name__, 17 | GIFSaveOptions.__name__, 18 | JPEGSaveOptions.__name__, 19 | PDFSaveOptions.__name__, 20 | ExportOptionsSaveForWeb.__name__, 21 | PNGSaveOptions.__name__, 22 | PhotoshopSaveOptions.__name__, 23 | TargaSaveOptions.__name__, 24 | TiffSaveOptions.__name__, 25 | ] 26 | -------------------------------------------------------------------------------- /photoshop/api/save_options/bmp.py: -------------------------------------------------------------------------------- 1 | """Options for saving a document in BMO format.""" 2 | 3 | # Import local modules 4 | from photoshop.api._core import Photoshop 5 | 6 | 7 | class BMPSaveOptions(Photoshop): 8 | """Options for saving a document in BMP format.""" 9 | 10 | object_name = "BMPSaveOptions" 11 | 12 | def __init__(self): 13 | super().__init__() 14 | 15 | @property 16 | def alphaChannels(self): 17 | """State to save the alpha channels.""" 18 | return self.app.alphaChannels 19 | 20 | @alphaChannels.setter 21 | def alphaChannels(self, value): 22 | """Sets whether to save the alpha channels or not. 23 | 24 | Args: 25 | 26 | """ 27 | self.app.alphaChannels = value 28 | -------------------------------------------------------------------------------- /photoshop/api/save_options/eps.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class EPSSaveOptions(Photoshop): 6 | """Options for saving a document in EPS format. 7 | 8 | using the `Document.saveAs()` 9 | 10 | """ 11 | 12 | object_name = "EPSSaveOptions" 13 | 14 | def __init__(self): 15 | super().__init__() 16 | 17 | @property 18 | def embedColorProfile(self) -> bool: 19 | """True to embed the color profile in this document.""" 20 | return self.app.embedColorProfile 21 | 22 | @embedColorProfile.setter 23 | def embedColorProfile(self, boolean: bool): 24 | """True to embed the color profile in this document.""" 25 | self.app.embedColorProfile = boolean 26 | 27 | @property 28 | def encoding(self): 29 | return self.app.encoding 30 | 31 | @encoding.setter 32 | def encoding(self, value: bool): 33 | self.app.encoding = value 34 | 35 | @property 36 | def halftoneScreen(self): 37 | return self.app.halftoneScreen 38 | 39 | @halftoneScreen.setter 40 | def halftoneScreen(self, value: bool): 41 | self.app.halftoneScreen = value 42 | 43 | @property 44 | def interpolation(self): 45 | return self.app.interpolation 46 | 47 | @interpolation.setter 48 | def interpolation(self, value: bool): 49 | self.app.interpolation = value 50 | 51 | @property 52 | def preview(self): 53 | return self.app.preview 54 | 55 | @preview.setter 56 | def preview(self, value: bool): 57 | self.app.preview = value 58 | 59 | @property 60 | def psColorManagement(self): 61 | return self.app.psColorManagement 62 | 63 | @psColorManagement.setter 64 | def psColorManagement(self, value: bool): 65 | self.app.psColorManagement = value 66 | 67 | @property 68 | def transferFunction(self): 69 | return self.app.transferFunction 70 | 71 | @transferFunction.setter 72 | def transferFunction(self, value: bool): 73 | self.app.transferFunction = value 74 | 75 | @property 76 | def transparentWhites(self) -> bool: 77 | """True to display white areas as transparent""" 78 | return self.app.transparentWhites 79 | 80 | @transparentWhites.setter 81 | def transparentWhites(self, value: bool): 82 | """True to display white areas as transparent""" 83 | self.app.transparentWhites = value 84 | 85 | @property 86 | def vectorData(self): 87 | """True to include vector data.""" 88 | return self.app.vectorData 89 | 90 | @vectorData.setter 91 | def vectorData(self, value: bool): 92 | """True to include vector data. 93 | 94 | Valid only if the document includes vector data (text). 95 | 96 | """ 97 | self.app.vectorData = value 98 | -------------------------------------------------------------------------------- /photoshop/api/save_options/gif.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class GIFSaveOptions(Photoshop): 6 | """Options for saving a document in GIF format.""" 7 | 8 | object_name = "GIFSaveOptions" 9 | 10 | def __init__(self): 11 | super().__init__() 12 | 13 | @property 14 | def colors(self): 15 | return self.app.color 16 | 17 | @colors.setter 18 | def colors(self, value): 19 | self.app.colors = value 20 | 21 | @property 22 | def dither(self): 23 | return self.app.dither 24 | 25 | @dither.setter 26 | def dither(self, value): 27 | self.app.dither = value 28 | 29 | @property 30 | def ditherAmount(self): 31 | return self.app.ditherAmount 32 | 33 | @ditherAmount.setter 34 | def ditherAmount(self, value): 35 | self.app.ditherAmount = value 36 | 37 | @property 38 | def forced(self): 39 | return self.app.forced 40 | 41 | @forced.setter 42 | def forced(self, value): 43 | self.app.forced = value 44 | 45 | @property 46 | def interlaced(self): 47 | return self.app.interlaced 48 | 49 | @interlaced.setter 50 | def interlaced(self, value): 51 | self.app.interlaced = value 52 | 53 | @property 54 | def matte(self): 55 | return self.app.matte 56 | 57 | @matte.setter 58 | def matte(self, value): 59 | self.app.matte = value 60 | 61 | @property 62 | def palette(self): 63 | return self.app.palette 64 | 65 | @palette.setter 66 | def palette(self, value): 67 | self.app.palette = value 68 | 69 | @property 70 | def preserveExactColors(self): 71 | return self.app.preserveExactColors 72 | 73 | @preserveExactColors.setter 74 | def preserveExactColors(self, value): 75 | self.app.preserveExactColors = value 76 | 77 | @property 78 | def transparency(self): 79 | return self.app.transparency 80 | 81 | @transparency.setter 82 | def transparency(self, value): 83 | self.app.transparency = value 84 | -------------------------------------------------------------------------------- /photoshop/api/save_options/jpg.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | from photoshop.api.enumerations import MatteType 4 | 5 | 6 | class JPEGSaveOptions(Photoshop): 7 | """Options for saving a document in JPEG format.""" 8 | 9 | object_name = "JPEGSaveOptions" 10 | 11 | def __init__(self, quality=5, embedColorProfile=True, matte=MatteType.NoMatte): 12 | super().__init__() 13 | self.quality = quality 14 | self.embedColorProfile = embedColorProfile 15 | self.matte = matte 16 | 17 | @property 18 | def quality(self): 19 | return self.app.quality 20 | 21 | @quality.setter 22 | def quality(self, value): 23 | self.app.quality = value 24 | 25 | @property 26 | def formatOptions(self): 27 | """The download format to use.""" 28 | return self.app.formatOptions 29 | 30 | @formatOptions.setter 31 | def formatOptions(self, value): 32 | self.app.formatOptions = value 33 | 34 | @property 35 | def embedColorProfile(self): 36 | return self.app.embedColorProfile 37 | 38 | @embedColorProfile.setter 39 | def embedColorProfile(self, value): 40 | self.app.embedColorProfile = value 41 | 42 | @property 43 | def matte(self): 44 | """The color to use to fill anti-aliased edges adjacent to 45 | transparent""" 46 | return self.app.matte 47 | 48 | @matte.setter 49 | def matte(self, value): 50 | self.app.matte = value 51 | 52 | @property 53 | def scans(self): 54 | return self.app.scans 55 | 56 | @scans.setter 57 | def scans(self, value): 58 | self.app.scans = value 59 | -------------------------------------------------------------------------------- /photoshop/api/save_options/png.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | from photoshop.api.enumerations import ColorReductionType 4 | from photoshop.api.enumerations import DitherType 5 | from photoshop.api.enumerations import SaveDocumentType 6 | 7 | 8 | class ExportOptionsSaveForWeb(Photoshop): 9 | """Options for exporting Save For Web files.""" 10 | 11 | object_name = "ExportOptionsSaveForWeb" 12 | 13 | def __init__(self): 14 | super().__init__() 15 | self._format = SaveDocumentType.PNGSave # Default to PNG 16 | self.PNG8 = False # Sets it to PNG-24 bit 17 | 18 | @property 19 | def format(self) -> SaveDocumentType: 20 | """The file format to use. One of the SaveDocumentType constants.""" 21 | return self._format 22 | 23 | @format.setter 24 | def format(self, value: SaveDocumentType): 25 | """Set the file format to use.""" 26 | self._format = value 27 | 28 | @property 29 | def PNG8(self) -> bool: 30 | """If true, uses 8 bits. If false, uses 24 bits. Valid only when 'format' = PNG.""" 31 | return self.app.PNG8 32 | 33 | @PNG8.setter 34 | def PNG8(self, value: bool): 35 | self.app.PNG8 = value 36 | 37 | @property 38 | def blur(self) -> float: 39 | """Applies blur to the image to reduce artifacts.""" 40 | return self.app.blur 41 | 42 | @blur.setter 43 | def blur(self, value: float): 44 | self.app.blur = value 45 | 46 | @property 47 | def colorReduction(self) -> ColorReductionType: 48 | """The color reduction algorithm.""" 49 | return self.app.colorReduction 50 | 51 | @colorReduction.setter 52 | def colorReduction(self, value: ColorReductionType): 53 | self.app.colorReduction = value 54 | 55 | @property 56 | def colors(self) -> int: 57 | """The number of colors in the palette.""" 58 | return self.app.colors 59 | 60 | @colors.setter 61 | def colors(self, value: int): 62 | self.app.colors = value 63 | 64 | @property 65 | def dither(self) -> DitherType: 66 | """The type of dither to use.""" 67 | return self.app.dither 68 | 69 | @dither.setter 70 | def dither(self, value: DitherType): 71 | self.app.dither = value 72 | 73 | @property 74 | def optimized(self) -> bool: 75 | """If true, optimization is enabled.""" 76 | return self.app.optimized 77 | 78 | @optimized.setter 79 | def optimized(self, value: bool): 80 | self.app.optimized = value 81 | 82 | @property 83 | def quality(self) -> int: 84 | """The quality of the output image, from 0 to 100.""" 85 | return self.app.quality 86 | 87 | @quality.setter 88 | def quality(self, value: int): 89 | self.app.quality = value 90 | 91 | 92 | class PNGSaveOptions(Photoshop): 93 | """Options for saving file as PNG.""" 94 | 95 | object_name = "PNGSaveOptions" 96 | 97 | def __init__(self, interlaced: bool = False, compression: int = 6): 98 | super().__init__() 99 | self.interlaced = interlaced 100 | self.compression = compression 101 | 102 | @property 103 | def interlaced(self) -> bool: 104 | return self.app.interlaced 105 | 106 | @interlaced.setter 107 | def interlaced(self, value: bool): 108 | self.app.interlaced = value 109 | 110 | @property 111 | def compression(self) -> int: 112 | return self.app.compression 113 | 114 | @compression.setter 115 | def compression(self, value: int): 116 | self.app.compression = value 117 | -------------------------------------------------------------------------------- /photoshop/api/save_options/psd.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class PhotoshopSaveOptions(Photoshop): 6 | """Options for saving a Photoshop document.""" 7 | 8 | object_name = "PhotoshopSaveOptions" 9 | 10 | def __int__(self): 11 | super().__init__() 12 | 13 | @property 14 | def alphaChannels(self): 15 | """If true, the alpha channels are saved.""" 16 | return self.app.alphaChannels() 17 | 18 | @alphaChannels.setter 19 | def alphaChannels(self, value): 20 | self.app.alphaChannels = value 21 | 22 | @property 23 | def annotations(self): 24 | """If true, the annotations are saved.""" 25 | return self.app.annotations() 26 | 27 | @annotations.setter 28 | def annotations(self, value): 29 | self.app.annotations = value 30 | 31 | @property 32 | def embedColorProfile(self): 33 | """If true, the color profile is embedded in the document.""" 34 | return self.app.embedColorProfile() 35 | 36 | @embedColorProfile.setter 37 | def embedColorProfile(self, value): 38 | self.app.embedColorProfile = value 39 | 40 | @property 41 | def layers(self): 42 | """If true, the layers are saved.""" 43 | return self.app.layers() 44 | 45 | @layers.setter 46 | def layers(self, value): 47 | self.app.layers = value 48 | 49 | @property 50 | def spotColors(self): 51 | """If true, spot colors are saved.""" 52 | return self.app.spotColors() 53 | 54 | @spotColors.setter 55 | def spotColors(self, value): 56 | self.app.spotColors = value 57 | -------------------------------------------------------------------------------- /photoshop/api/save_options/tag.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | from photoshop.api.enumerations import TargaBitsPerPixels 4 | 5 | 6 | class TargaSaveOptions(Photoshop): 7 | """Options for saving a document in TGA (Targa) format.""" 8 | 9 | object_name = "TargaSaveOptions" 10 | 11 | def __int__(self): 12 | super().__init__() 13 | 14 | @property 15 | def alphaChannels(self): 16 | """If true, the alpha channels are saved.""" 17 | return self.app.alphaChannels 18 | 19 | @alphaChannels.setter 20 | def alphaChannels(self, value): 21 | self.app.alphaChannels = value 22 | 23 | @property 24 | def resolution(self): 25 | return self.app.resolution 26 | 27 | @resolution.setter 28 | def resolution(self, value: TargaBitsPerPixels = TargaBitsPerPixels.Targa24Bits): 29 | self.app.resolution = value 30 | 31 | @property 32 | def rleCompression(self): 33 | return self.app.rleCompression 34 | 35 | @rleCompression.setter 36 | def rleCompression(self, value): 37 | self.app.rleCompression = value 38 | -------------------------------------------------------------------------------- /photoshop/api/save_options/tif.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class TiffSaveOptions(Photoshop): 6 | """Options for saving a document in TIFF format.""" 7 | 8 | object_name = "TiffSaveOptions" 9 | 10 | def __int__(self): 11 | super().__init__() 12 | 13 | @property 14 | def alphaChannels(self): 15 | """If true, the alpha channels are saved.""" 16 | return self.app.alphaChannels 17 | 18 | @alphaChannels.setter 19 | def alphaChannels(self, value): 20 | self.app.alphaChannels = value 21 | 22 | @property 23 | def annotations(self): 24 | """If true, the annotations are saved.""" 25 | return self.app.annotations 26 | 27 | @annotations.setter 28 | def annotations(self, value): 29 | self.app.annotations = value 30 | 31 | @property 32 | def byteOrder(self): 33 | """The order in which the bytes will be read. 34 | Default: 35 | Mac OS when running in Mac OS, and IBM PC when running in Windows. 36 | """ 37 | return self.app.byteOrder 38 | 39 | @byteOrder.setter 40 | def byteOrder(self, value): 41 | self.app.byteOrder = value 42 | 43 | @property 44 | def embedColorProfile(self): 45 | """If true, the color profile is embedded in the document.""" 46 | return self.app.embedColorProfile 47 | 48 | @embedColorProfile.setter 49 | def embedColorProfile(self, value): 50 | self.app.embedColorProfile = value 51 | 52 | @property 53 | def imageCompression(self): 54 | """The compression type.""" 55 | return self.app.imageCompression 56 | 57 | @imageCompression.setter 58 | def imageCompression(self, value): 59 | self.app.imageCompression = value 60 | 61 | @property 62 | def interleaveChannels(self): 63 | """If true, the channels in the image are interleaved.""" 64 | return self.app.interleaveChannels 65 | 66 | @interleaveChannels.setter 67 | def interleaveChannels(self, value): 68 | self.app.interleaveChannels = value 69 | 70 | @property 71 | def jpegQuality(self): 72 | """The quality of the produced image, which is inversely proportionate 73 | to the amount of JPEG compression. 74 | Valid only for JPEG compressed TIFF documents. Range: 0 to 12. 75 | """ 76 | return self.app.jpegQuality 77 | 78 | @jpegQuality.setter 79 | def jpegQuality(self, value): 80 | self.app.jpegQuality = value 81 | 82 | @property 83 | def layerCompression(self): 84 | return self.app.layerCompression 85 | 86 | @layerCompression.setter 87 | def layerCompression(self, value): 88 | """The method of compression to use when saving layers 89 | (as opposed to saving composite data). 90 | Valid only when `layers` = true. 91 | """ 92 | self.app.layerCompression = value 93 | 94 | @property 95 | def layers(self): 96 | """If true, the layers are saved.""" 97 | return self.app.layers 98 | 99 | @layers.setter 100 | def layers(self, value): 101 | self.app.layers = value 102 | 103 | @property 104 | def saveImagePyramid(self): 105 | """If true, preserves multi-resolution information.""" 106 | return self.app.saveImagePyramid 107 | 108 | @saveImagePyramid.setter 109 | def saveImagePyramid(self, value): 110 | self.app.saveImagePyramid = value 111 | 112 | @property 113 | def spotColors(self): 114 | """If true, spot colors are saved.""" 115 | return self.app.spotColors 116 | 117 | @spotColors.setter 118 | def spotColors(self, value): 119 | self.app.spotColors = value 120 | 121 | @property 122 | def transparency(self): 123 | return self.app.transparency 124 | 125 | @transparency.setter 126 | def transparency(self, value): 127 | """If true, saves the transparency as an additional alpha channel when 128 | the file is opened in another application.""" 129 | self.app.transparency = value 130 | -------------------------------------------------------------------------------- /photoshop/api/solid_color.py: -------------------------------------------------------------------------------- 1 | """A color definition used in the document. 2 | 3 | Maps a color to equivalents in all available color models. 4 | 5 | - Used in `Application.backgroundColor` and `foregroundColor` properties, in 6 | `Channel.color`, in `ColorSampler.color`, and in `TextItem.color` 7 | - Passed to `PathItem.fillPath()`, `Selection.fill()`, and `Selection.stroke()`. 8 | 9 | """ 10 | 11 | # Import local modules 12 | from photoshop.api._core import Photoshop 13 | from photoshop.api.colors.cmyk import CMYKColor 14 | from photoshop.api.colors.gray import GrayColor 15 | from photoshop.api.colors.hsb import HSBColor 16 | from photoshop.api.colors.lab import LabColor 17 | from photoshop.api.colors.rgb import RGBColor 18 | from photoshop.api.enumerations import ColorModel 19 | 20 | 21 | class SolidColor(Photoshop): 22 | """A color definition used in the document.""" 23 | 24 | object_name = "SolidColor" 25 | 26 | def __init__(self, parent=None): 27 | super().__init__(parent=parent) 28 | self._flag_as_method( 29 | "isEqual", 30 | ) 31 | 32 | @property 33 | def cmyk(self) -> CMYKColor: 34 | """The CMYK color mode. 35 | 36 | Returns: 37 | .colors.cmyk.CMYKColor: 38 | 39 | """ 40 | return CMYKColor(self.app.cmyk) 41 | 42 | @cmyk.setter 43 | def cmyk(self, value: CMYKColor): 44 | self.app.cmyk = value 45 | 46 | @property 47 | def gray(self) -> GrayColor: 48 | return GrayColor(self.app.gray) 49 | 50 | @property 51 | def hsb(self) -> HSBColor: 52 | return HSBColor(self.app.hsb) 53 | 54 | @hsb.setter 55 | def hsb(self, value: HSBColor): 56 | self.app.hsb = value 57 | 58 | @property 59 | def lab(self) -> LabColor: 60 | return LabColor(self.app.lab) 61 | 62 | @lab.setter 63 | def lab(self, value: LabColor): 64 | self.app.lab = value 65 | 66 | @property 67 | def model(self) -> ColorModel: 68 | """The color model.""" 69 | return ColorModel(self.app.model) 70 | 71 | @model.setter 72 | def model(self, value: ColorModel): 73 | """The color model.""" 74 | self.app.model = value 75 | 76 | @property 77 | def nearestWebColor(self) -> RGBColor: 78 | """The nearest web color to the current color.""" 79 | return RGBColor(self.app.NearestWebColor) 80 | 81 | @property 82 | def rgb(self) -> RGBColor: 83 | """The RGB color mode.""" 84 | return RGBColor(self.app.rgb) 85 | 86 | @rgb.setter 87 | def rgb(self, value: RGBColor): 88 | self.app.rgb = value 89 | 90 | def isEqual(self, color: RGBColor): 91 | """`SolidColor` object is visually equal to the specified color.""" 92 | return self.app.isEqual(color) 93 | -------------------------------------------------------------------------------- /photoshop/api/text_font.py: -------------------------------------------------------------------------------- 1 | # Import local modules 2 | from photoshop.api._core import Photoshop 3 | 4 | 5 | class TextFont(Photoshop): 6 | """An installed font.""" 7 | 8 | def __init__(self, parent=None): 9 | super().__init__(parent=parent) 10 | 11 | @property 12 | def family(self) -> str: 13 | """The font family""" 14 | return self.app.family 15 | 16 | @property 17 | def name(self): 18 | """The name of the font.""" 19 | return self.app.name 20 | 21 | @property 22 | def postScriptName(self): 23 | """The PostScript name of the font.""" 24 | return self.app.postScriptName 25 | 26 | @property 27 | def style(self): 28 | """The font style.""" 29 | return self.app.style 30 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "photoshop-python-api" 3 | version = "0.24.1" 4 | description = "Python API for Photoshop." 5 | homepage = "https://github.com/loonghao/photoshop-python-api" 6 | repository = "https://github.com/loonghao/photoshop-python-api" 7 | documentation = "https://photoshop-python-api.readthedocs.io/en/master/" 8 | keywords = ["python-api, photoshop-python-api", "photoshop", "python"] 9 | authors = ["longhao "] 10 | license = "MIT" 11 | readme = "README.md" 12 | classifiers = [ 13 | "Development Status :: 4 - Beta", 14 | "License :: OSI Approved :: MIT License", 15 | "Operating System :: Microsoft :: Windows", 16 | "Operating System :: Microsoft :: Windows :: Windows 10", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Programming Language :: Python :: 3.8", 19 | "Programming Language :: Python :: 3.9", 20 | "Programming Language :: Python :: 3.10", 21 | "Programming Language :: Python :: 3.11", 22 | "Programming Language :: Python :: 3.12", 23 | ] 24 | 25 | packages = [ 26 | { include = "photoshop" }, 27 | ] 28 | [tool.poetry.build] 29 | generate-setup-file = false 30 | 31 | [tool.poetry.dependencies] 32 | python = ">=3.8,<4.0" 33 | wheel = "^0.45.0" 34 | comtypes = "^1.1.11" 35 | 36 | [tool.poetry.group.dev.dependencies] 37 | commitizen = "^2.17.8" 38 | pre-commit = "^2.13.0" 39 | codecov = "^2.1.11" 40 | pylint = "^2.8.2" 41 | isort = "^5.8.0" 42 | pytest = "^8.0.0" 43 | flake8 = "^4.0.0" 44 | mypy = "^1.0" 45 | coverage = "^7.0.0" 46 | mkdocs = "^1.2.2" 47 | mkdocs-git-revision-date-plugin = "^0.3.1" 48 | black = "^23.0.0" 49 | mkdocs-material = "^8.2.5" 50 | mkdocstrings-python = "^1.0.0" 51 | mkdocs-pymdownx-material-extras = "^2.0" 52 | mkdocs-same-dir = "^0.1.1" 53 | mkdocs-include-markdown-plugin = "^3.3.0" 54 | mkdocs-gen-files = "^0.5.0" 55 | mkdocs-autolinks-plugin = "^0.7.0" 56 | mkdocs-minify-plugin = "^0.8.0" 57 | mkdocs-git-revision-date-localized-plugin = "^1.0.0" 58 | pytest-cov = "^4.0.0" 59 | stringcase = "^1.2.0" 60 | mkdocs-literate-nav = "^0.6.0" 61 | 62 | [tool.commitizen] 63 | name = "cz_conventional_commits" 64 | version = "0.24.1" 65 | tag_format = "v$version" 66 | version_files = [ 67 | "pyproject.toml:version", 68 | "photoshop/__version__.py" 69 | ] 70 | 71 | [build-system] 72 | requires = ["poetry-core>=1.0.0"] 73 | build-backend = "poetry.core.masonry.api" 74 | 75 | [tool.black] 76 | line-length = 120 77 | target_version = ['py37'] 78 | include = '\.pyi?$' 79 | exclude = ''' 80 | 81 | ( 82 | /( 83 | \.eggs # exclude a few common directories in the 84 | | \.git # root of the project 85 | | \.hg 86 | | \.mypy_cache 87 | | \.tox 88 | | \.venv 89 | | _build 90 | | buck-out 91 | | build 92 | | dist 93 | )/ 94 | | foo.py # also separately exclude a file named foo.py in 95 | # the root of the project 96 | ) 97 | ''' 98 | 99 | [tool.isort] 100 | profile = "black" 101 | atomic = true 102 | include_trailing_comma = true 103 | lines_after_imports = 2 104 | lines_between_types = 1 105 | use_parentheses = true 106 | src_paths = ["photoshop", "test"] 107 | filter_files = true 108 | known_first_party = "photoshop" 109 | 110 | # Enforce import section headers. 111 | import_heading_future = "Import future modules" 112 | import_heading_stdlib = "Import built-in modules" 113 | import_heading_thirdparty = "Import third-party modules" 114 | import_heading_firstparty = "Import local modules" 115 | 116 | force_sort_within_sections = true 117 | force_single_line = true 118 | 119 | # All project unrelated unknown imports belong to third-party. 120 | default_section = "THIRDPARTY" 121 | skip_glob = "*/docs/conf.py" 122 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | # Import built-in modules 2 | import os 3 | 4 | # Import third-party modules 5 | import pytest 6 | 7 | 8 | @pytest.fixture() 9 | def photoshop_app(): 10 | # Import local modules 11 | from photoshop.api import Application 12 | 13 | app = Application() 14 | app.documents.add(name="UnitTest") 15 | yield app 16 | app.activeDocument.close() 17 | 18 | 19 | @pytest.fixture() 20 | def data_root(): 21 | return os.path.join(os.path.dirname(__file__), "test_data") 22 | 23 | 24 | @pytest.fixture() 25 | def psd_file(data_root): 26 | def _get_psd_file(name): 27 | return os.path.join(data_root, f"{name}.psd") 28 | 29 | return _get_psd_file 30 | -------------------------------------------------------------------------------- /test/manual_test/manual_test_all_examples.py: -------------------------------------------------------------------------------- 1 | """Manual test all examples.""" 2 | 3 | # Import built-in modules 4 | from pathlib import Path 5 | 6 | # Import local modules 7 | from photoshop.api import Application 8 | 9 | 10 | root = Path(__file__).parent.parent.parent.joinpath("examples") 11 | for script_file in root.glob("*.py"): 12 | try: 13 | exec(script_file.read_text()) 14 | except Exception as err: 15 | print(f"Test failed: {script_file}", str(err), end="\n") 16 | 17 | # Clear up and close all documents. 18 | app = Application() 19 | 20 | while app.documents.length: 21 | app.activeDocument.close() 22 | -------------------------------------------------------------------------------- /test/manual_test/manual_test_application.py: -------------------------------------------------------------------------------- 1 | """""" 2 | # Import third-party modules 3 | import pytest 4 | 5 | # Import local modules 6 | from photoshop.api import Application 7 | from photoshop.api import EventID 8 | from photoshop.api import SolidColor 9 | 10 | 11 | class TestApplication: 12 | """Test the solidColor.""" 13 | 14 | # pylint: disable=attribute-defined-outside-init 15 | @pytest.fixture(autouse=True) 16 | def setup(self): 17 | self.app = Application() 18 | color = SolidColor() 19 | color.rgb.red = 255 20 | color.rgb.green = 111 21 | self.app.backgroundColor = color 22 | foreground_color = SolidColor() 23 | foreground_color.rgb.green = 0 24 | self.app.foregroundColor = foreground_color 25 | self.app.currentTool = "moveTool" 26 | self.app.notifiers.removeAll() 27 | 28 | def test_active_document(self, photoshop_app): 29 | assert photoshop_app.activeDocument.name == self.app.activeDocument.name 30 | 31 | def test_get_background_color(self): 32 | assert self.app.backgroundColor.rgb.red == 255 33 | assert self.app.backgroundColor.rgb.green == 111 34 | assert self.app.backgroundColor.rgb.blue == 255 35 | assert self.app.backgroundColor.cmyk.yellow == 0 36 | assert self.app.backgroundColor.cmyk.magenta == 60 37 | assert self.app.backgroundColor.cmyk.cyan == 17 38 | assert self.app.backgroundColor.cmyk.black == 0 39 | assert self.app.backgroundColor.hsb.hue == 300 40 | assert self.app.backgroundColor.hsb.saturation == 56 41 | assert self.app.backgroundColor.hsb.brightness == 100 42 | assert self.app.backgroundColor.lab.A == 68 43 | assert self.app.backgroundColor.lab.B == -46 44 | assert self.app.backgroundColor.lab.L == 69 45 | 46 | def test_set_background_color(self, photoshop_app): 47 | self.app.backgroundColor.rgb.green = 0 48 | assert self.app.backgroundColor.rgb.green == photoshop_app.backgroundColor.rgb.green 49 | 50 | def test_build(self): 51 | assert self.app.build == "21.0 (20191018.r.37 2019/10/18: 614690fb487)" 52 | 53 | def test_get_color_settings(self): 54 | assert self.app.colorSettings == "North America General Purpose 2" 55 | 56 | def test_set_color_settings(self, photoshop_app): 57 | color_settings = photoshop_app.colorSettings 58 | self.app.colorSettings = "Monitor Color" 59 | assert photoshop_app.colorSettings == "Monitor Color" 60 | self.app.colorSettings = color_settings 61 | 62 | def test_get_current_tool(self): 63 | assert self.app.currentTool == "moveTool" 64 | 65 | def test_set_current_tool(self): 66 | self.app.currentTool = "typeCreateOrEditTool" 67 | assert self.app.currentTool == "typeCreateOrEditTool" 68 | 69 | def test_displayDialogs(self): 70 | assert str(self.app.displayDialogs) == "DialogModes.DisplayErrorDialogs" 71 | 72 | def test_documents(self): 73 | assert len(self.app.documents) == 0 74 | 75 | def test_get_fonts_count(self): 76 | assert self.app.fonts.length == 440 77 | assert len(self.app.fonts) == 440 78 | 79 | def test_get_foreground_color(self): 80 | assert self.app.foregroundColor.rgb.red == 255 81 | assert self.app.foregroundColor.rgb.green == 0 82 | assert self.app.foregroundColor.rgb.blue == 255 83 | assert self.app.foregroundColor.cmyk.yellow == 0 84 | assert self.app.foregroundColor.cmyk.magenta == 82 85 | assert self.app.foregroundColor.cmyk.cyan == 27 86 | assert self.app.foregroundColor.cmyk.black == 0 87 | assert self.app.foregroundColor.hsb.hue == 300 88 | assert self.app.foregroundColor.hsb.saturation == 100 89 | assert self.app.foregroundColor.hsb.brightness == 100 90 | assert self.app.foregroundColor.lab.A == 93 91 | assert self.app.foregroundColor.lab.B == -61 92 | assert self.app.foregroundColor.lab.L == 60 93 | 94 | def test_get_free_memory(self): 95 | assert self.app.freeMemory 96 | 97 | def test_get_locale(self): 98 | assert self.app.locale == "en_US" 99 | 100 | def test_macintoshFileTypes(self): 101 | assert "JPEG" in self.app.macintoshFileTypes 102 | 103 | def test_get_name(self): 104 | assert self.app.name == "Adobe Photoshop" 105 | 106 | def test_notifiers(self): 107 | assert self.app.notifiers.length == 0 108 | 109 | def test_add_notifiers(self, tmpdir): 110 | jsx_file = tmpdir.join("event.jsx") 111 | jsx_file.write('alert("Test Event")') 112 | self.app.notifiers.add(EventID.Open, str(jsx_file)) 113 | assert self.app.notifiers.length == 1 114 | assert self.app.notifiers[0].EventID == EventID.Open 115 | 116 | def test_get_notifiersEnabled(self): 117 | assert not self.app.notifiersEnabled 118 | 119 | def test_get_application_path(self): 120 | assert self.app.path.as_posix() == "C:/Program Files/Adobe/Adobe Photoshop 2020" 121 | 122 | def test_playbackDisplayDialogs(self): 123 | assert self.app.playbackDisplayDialogs == "DialogModes.NO" 124 | 125 | def test_playbackParameters(self): 126 | # assert self.app.playbackParameters 127 | print("Need test.") 128 | 129 | def test_preferences(self): 130 | assert self.app.preferences 131 | 132 | def test_get_preferencesFolder(self): 133 | assert self.app.preferencesFolder.is_dir() 134 | 135 | def test_get_recentFiles(self): 136 | assert self.app.recentFiles 137 | 138 | def test_scriptingBuildDate(self): 139 | assert self.app.scriptingBuildDate == "Apr 10 2020 00:39:52" 140 | 141 | def test_get_scriptingVersion(self): 142 | assert self.app.scriptingVersion == "21.1" 143 | 144 | def test_get_systemInformation(self): 145 | assert self.app.systemInformation 146 | 147 | def test_get_typename(self): 148 | assert self.app.typename == "Application" 149 | 150 | def test_get_version(self): 151 | assert self.app.version == "21.1.2" 152 | 153 | def test_windowsFileTypes(self): 154 | assert len(self.app.windowsFileTypes) >= 100 155 | 156 | def test_batch(self): 157 | self.app.batch() 158 | 159 | def test_beep(self): 160 | self.app.beep() 161 | 162 | def test_bringToFront(self): 163 | self.app.bringToFront() 164 | 165 | def test_changeProgressText(self): 166 | self.app.changeProgressText("test") 167 | 168 | def test_charIDToTypeID(self): 169 | assert self.app.charIDToTypeID("Type") == "1417244773" 170 | 171 | def test_compareWithNumbers(self): 172 | assert self.app.compareWithNumbers(20, 1) 173 | 174 | def test_do_action(self): 175 | self.app.doAction("Vignette (selection)", "Default Actions") 176 | 177 | def test_featureEnabled(self): 178 | assert self.app.featureEnabled("photoshop/extended") 179 | 180 | # def test_getCustomOptions(self): 181 | # assert self.app.getCustomOptions("Application") 182 | 183 | def test_isQuicktimeAvailable(self): 184 | assert self.app.isQuicktimeAvailable 185 | 186 | def test_openDialog(self): 187 | assert self.app.openDialog() 188 | 189 | def test_refresh(self): 190 | return self.app.refresh() 191 | 192 | def test_refreshFonts(self): 193 | return self.app.refreshFonts() 194 | 195 | def test_run_menu_item(self): 196 | assert self.app.runMenuItem(self.app.stringIDToTypeID("toggleProofColors")) 197 | 198 | def test_showColorPicker(self): 199 | assert self.app.showColorPicker() 200 | 201 | def test_stringIDToTypeID(self): 202 | assert self.app.stringIDToTypeID("toggleProofColors") == 2034 203 | 204 | def test_togglePalettes(self): 205 | assert self.app.togglePalettes() 206 | self.app.togglePalettes() 207 | 208 | def test_toolSupportsBrushes(self): 209 | assert self.app.toolSupportsBrushes("Tool") 210 | 211 | def test_toolSupportsBrushPresets(self): 212 | assert self.app.toolSupportsBrushPresets("Tool") 213 | 214 | def test_typeIDToCharID(self): 215 | assert self.app.typeIDToCharID("toggleProofColors") 216 | 217 | def test_typeIDToStringID(self): 218 | assert self.app.typeIDToStringID("toggleProofColors") 219 | 220 | def test_updateProgress(self): 221 | assert self.app.updateProgress("Done", "total") 222 | -------------------------------------------------------------------------------- /test/manual_test/manual_test_layer_comps.py: -------------------------------------------------------------------------------- 1 | """""" 2 | # Import third-party modules 3 | import pytest 4 | 5 | # Import local modules 6 | from photoshop import Session 7 | 8 | 9 | class TestTextItem: 10 | """Test the solidColor.""" 11 | 12 | # pylint: disable=attribute-defined-outside-init 13 | @pytest.fixture(autouse=True) 14 | def setup(self, psd_file): 15 | """Setup for current test.""" 16 | self.session = Session(file_path=psd_file("layer_comps"), action="open", auto_close=True) 17 | self.session.run_action() 18 | doc = self.session.active_document 19 | self.layer_compse = doc.layerComps # -> TextItem 20 | yield 21 | self.session.close() 22 | 23 | def test_length(self): 24 | assert self.layer_compse.length == 2 25 | 26 | def test_getByName(self): 27 | layer = self.layer_compse.getByName("layer1") 28 | assert layer.name == "layer1" 29 | 30 | def test_loop_layers(self): 31 | for layer in self.layer_compse: 32 | assert layer.name 33 | 34 | def test_add_a_layer(self): 35 | self.layer_compse.add("new_layer", "test") 36 | assert self.layer_compse.length == 3 37 | -------------------------------------------------------------------------------- /test/manual_test/manual_test_solid_color.py: -------------------------------------------------------------------------------- 1 | """""" 2 | # Import third-party modules 3 | import pytest 4 | 5 | # Import local modules 6 | from photoshop import Session 7 | 8 | 9 | class TestSolidColor: 10 | """Test the solidColor.""" 11 | 12 | # pylint: disable=attribute-defined-outside-init 13 | @pytest.fixture(autouse=True) 14 | def setup(self): 15 | self.session = Session() 16 | self.solid_color = self.session.SolidColor() 17 | 18 | def test_cmyk_black(self): 19 | assert self.solid_color.cmyk.black == 0 20 | 21 | def test_cmyk_cyan(self): 22 | assert self.solid_color.cmyk.cyan == 0 23 | 24 | def test_cmyk_magenta(self): 25 | assert self.solid_color.cmyk.magenta == 0 26 | 27 | def test_cmyk_typename(self): 28 | assert self.solid_color.cmyk.typename == "CMYKColor" 29 | 30 | def test_yellow(self): 31 | assert self.solid_color.cmyk.yellow == 0 32 | 33 | def test_hsb_brightness(self): 34 | assert self.solid_color.hsb.brightness == 100 35 | 36 | def test_hsb_hue(self): 37 | assert self.solid_color.hsb.hue == 0 38 | 39 | def test_hsb_saturation(self): 40 | assert self.solid_color.hsb.saturation == 0 41 | 42 | def test_hsb_typename(self): 43 | assert self.solid_color.hsb.typename == "HSBColor" 44 | -------------------------------------------------------------------------------- /test/manual_test/manual_test_text_item.py: -------------------------------------------------------------------------------- 1 | """""" 2 | # Import third-party modules 3 | import pytest 4 | 5 | # Import local modules 6 | from photoshop import Session 7 | from photoshop.api.enumerations import TextType 8 | 9 | 10 | class TestTextItem: 11 | """Test the solidColor.""" 12 | 13 | # pylint: disable=attribute-defined-outside-init 14 | @pytest.fixture(autouse=True) 15 | def setup(self, psd_file): 16 | """Setup for current test.""" 17 | self.session = Session(file_path=psd_file("textitem"), action="open", auto_close=True) 18 | self.session.run_action() 19 | doc = self.session.active_document 20 | layer = doc.activeLayer 21 | self.text_item = layer.textItem() # -> TextItem 22 | yield 23 | # self.session.close() 24 | 25 | def manual_test_alternateLigatures(self): 26 | assert self.text_item.alternateLigatures == 0 27 | 28 | def test_antiAliasMethod(self): 29 | assert self.text_item.antiAliasMethod == 3 30 | 31 | def test_autoKerning(self): 32 | assert self.text_item.autoKerning == 2 33 | 34 | def test_autoLeadingAmount(self): 35 | assert self.text_item.autoLeadingAmount == 120.00000476837158 36 | 37 | def test_set_autoLeadingAmount(self): 38 | self.text_item.autoLeadingAmount = 20 39 | assert self.text_item.autoLeadingAmount == 20.000000298023224 40 | 41 | def test_baseline_shift(self): 42 | assert self.text_item.baselineShift == 0.0 43 | 44 | def test_fauxBold(self): 45 | assert not self.text_item.fauxBold 46 | 47 | def test_set_fauxBold(self): 48 | assert not self.text_item.fauxBold 49 | self.text_item.fauxBold = True 50 | assert self.text_item.fauxBold 51 | 52 | def test_fauxItalic(self): 53 | assert not self.text_item.fauxItalic 54 | 55 | def test_firstLineIndent(self): 56 | assert self.text_item.firstLineIndent == 0.0 57 | 58 | def test_get_font(self): 59 | assert self.text_item.font == "ArialMT" 60 | 61 | def test_set_font(self): 62 | self.text_item.font = "AdobeThai-Regular" 63 | assert self.text_item.font == "AdobeThai-Regular" 64 | 65 | def test_hangingPunctuation(self): 66 | assert not self.text_item.hangingPunctuation 67 | 68 | def test_hyphenateAfterFirst(self): 69 | assert self.text_item.hyphenateAfterFirst == 2 70 | 71 | def test_justification(self): 72 | assert self.text_item.justification == 1 73 | 74 | def test_set_justification(self): 75 | self.text_item.justification = 2 76 | assert self.text_item.justification == 2 77 | 78 | def test_kind(self): 79 | assert self.text_item.kind == 1 80 | 81 | def test_set_kind(self): 82 | self.text_item.kind = TextType.ParagraphText 83 | assert self.text_item.kind == 2 84 | assert self.text_item.kind == TextType.ParagraphText 85 | 86 | def test_noBreak(self): 87 | assert not self.text_item.noBreak 88 | 89 | def test_position(self): 90 | assert self.text_item.position == (5.0, 57.0) 91 | 92 | def test_size(self): 93 | assert self.text_item.size == 18.0 94 | 95 | def test_change_size(self): 96 | self.text_item.size = 20 97 | assert self.text_item.size == 20.0 98 | -------------------------------------------------------------------------------- /test/test_data/layer_comps.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/test/test_data/layer_comps.psd -------------------------------------------------------------------------------- /test/test_data/textitem.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loonghao/photoshop-python-api/e51814f8a115c645b874dc6a5187c7abe475f7cf/test/test_data/textitem.psd -------------------------------------------------------------------------------- /test/test_imports.py: -------------------------------------------------------------------------------- 1 | """Import Test.""" 2 | 3 | # Import future modules 4 | from __future__ import absolute_import 5 | from __future__ import division 6 | from __future__ import print_function 7 | 8 | # Import built-in modules 9 | import importlib 10 | import pkgutil 11 | 12 | # Import local modules 13 | import photoshop 14 | 15 | 16 | def test_imports(): 17 | """Test import modules.""" 18 | prefix = "{}.".format(photoshop.__name__) 19 | iter_packages = pkgutil.walk_packages( 20 | photoshop.__path__, # noqa: WPS609 21 | prefix, 22 | ) 23 | for _, name, _ in iter_packages: 24 | module_name = name if name.startswith(prefix) else prefix + name 25 | importlib.import_module(module_name) 26 | --------------------------------------------------------------------------------