This document provides detailed instructions for packaging and distributing the p2i application across different platforms.
- Project Structure
- Building Requirements
- Windows Distribution
- macOS Distribution
- Linux Distribution
- Setting Up Release Channels
- Continuous Integration
- Version Management
Before packaging, ensure your project structure is properly organized:
p2i/
├── main.py # Main application entry point
├── version.py # Centralized version string
├── settings.py # Settings management
├── styles.py # Modern UI theme and colors
├── utils.py # Utility functions
├── drag_drop.py # Drag and drop functionality
├── ghostscript_utils.py # Ghostscript detection and UI helpers
├── contribute_dialog.py # Contribution dialog
├── pdf_merge_tab.py # PDF tab modules
├── pdf_split_tab.py
├── pdf_compress_tab.py
├── pdf_to_image_tab.py
├── pdf_security_tab.py
├── pdf_organizer_tab.py
├── image_to_pdf_tab.py # Image tab modules
├── image_convert_tab.py
├── image_resize_tab.py
├── image_batch_tab.py
├── image_watermark_tab.py
├── image_metadata_tab.py
├── support_tab.py # Support/donation tab
├── resources/
│ └── icon/
│ ├── app_icon.ico # Windows icon
│ └── app_icon.png # Linux/macOS icon
├── installer.iss # Inno Setup installer script
├── LICENSE # MIT License
├── README.md # User documentation
├── DISTRIBUTION.md # This file
├── requirements.txt # Python dependencies
└── setup.py # Installation script
Create a setup.py file for proper installation:
from setuptools import setup, find_packages
setup(
name="p2i",
version="1.0.0",
description="Advanced PDF & Image Processing Tool",
author="Your Name",
author_email="your.email@example.com",
url="https://github.com/yourusername/p2i",
packages=find_packages(),
include_package_data=True,
install_requires=[
"pdf2image>=1.16.3",
"Pillow>=9.0.0",
"tqdm>=4.64.1",
"pypdfium2>=3.3.0",
"reportlab>=3.6.0",
"PyPDF2>=2.0.0",
],
entry_points={
"console_scripts": [
"p2i=p2i.main:main",
],
},
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.9",
"Topic :: Multimedia :: Graphics :: Editors",
"Topic :: Office/Business",
],
)Install the packaging tools needed for all platforms:
# Basic tools
pip install pyinstaller setuptools wheel twine
# For more advanced packaging
pip install cx_Freeze py2appPyInstaller is the easiest way to create standalone Windows executables:
# Install PyInstaller
pip install pyinstaller
# Basic build
pyinstaller --name=p2i --windowed main.py
# Optimized single-file build
pyinstaller --name=p2i --windowed --onefile --icon=icons/app_icon.ico --add-data="icons;icons" main.pyThe --windowed flag prevents a console window from appearing, and --onefile creates a single executable rather than a folder with dependencies.
For a professional installer, use NSIS (Nullsoft Scriptable Install System):
- Download and install NSIS
- Create an NSIS script (
p2i_installer.nsi):
!define APPNAME "p2i"
!define COMPANYNAME "Your Company"
!define DESCRIPTION "PDF & Image Processing Tool"
!define VERSIONMAJOR 1
!define VERSIONMINOR 0
!define VERSIONBUILD 0
# Define installer name
OutFile "p2i-setup-${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}.exe"
# Default installation directory
InstallDir "$PROGRAMFILES\${APPNAME}"
# Set compression
SetCompressor lzma
# Request application privileges
RequestExecutionLevel admin
Name "${APPNAME}"
Icon "icons\app_icon.ico"
Caption "${APPNAME} Installer"
# Default section
Section "Install"
# Set output path to the installation directory
SetOutPath $INSTDIR
# Add files
File /r "dist\p2i\*.*"
# Create uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
# Create desktop shortcut
CreateShortcut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\p2i.exe" "" "$INSTDIR\icons\app_icon.ico"
# Create start menu shortcut
CreateDirectory "$SMPROGRAMS\${APPNAME}"
CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\p2i.exe" "" "$INSTDIR\icons\app_icon.ico"
CreateShortcut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
# Write registry keys for uninstaller
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME} - ${DESCRIPTION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\icons\app_icon.ico"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${COMPANYNAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}"
SectionEnd
# Uninstaller section
Section "Uninstall"
# Remove files
RMDir /r "$INSTDIR"
# Remove shortcuts
Delete "$DESKTOP\${APPNAME}.lnk"
Delete "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk"
Delete "$SMPROGRAMS\${APPNAME}\Uninstall.lnk"
RMDir "$SMPROGRAMS\${APPNAME}"
# Remove registry keys
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
SectionEnd- Run NSIS compiler to build the installer:
- Right-click the .nsi file and select "Compile NSIS Script"
- Or use the command line:
makensis p2i_installer.nsi
For enterprise environments, MSI installers are preferred:
- Install the WiX Toolset
- Create a WiX source file (p2i.wxs) for your application
- Compile the WiX project to create an MSI package
- Using PyInstaller:
# Basic app bundle
pyinstaller --name=p2i --windowed --icon=icons/app_icon.icns main.py
# With specific macOS settings
pyinstaller --name=p2i --windowed --icon=icons/app_icon.icns \
--add-data="icons:icons" \
--osx-bundle-identifier=com.yourcompany.p2i \
main.py- Create a proper Info.plist:
Edit the generated Info.plist file in dist/p2i.app/Contents/ to add proper metadata.
Use the create-dmg tool to package your application:
# Install create-dmg
brew install create-dmg
# Create the DMG
create-dmg \
--volname "p2i Installer" \
--volicon "icons/app_icon.icns" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "p2i.app" 200 190 \
--hide-extension "p2i.app" \
--app-drop-link 600 185 \
"p2i-1.0.0.dmg" \
"dist/p2i.app"For distribution outside the App Store:
# Sign the app bundle
codesign --deep --force --verify --verbose --sign "Developer ID Application: Your Name (XXXXXXXXXX)" "dist/p2i.app"
# Verify signature
codesign --verify --deep --strict --verbose=2 "dist/p2i.app"
# Sign the DMG installer
codesign --force --verify --verbose --sign "Developer ID Application: Your Name (XXXXXXXXXX)" "p2i-1.0.0.dmg"For App Store distribution, use the App Store certificate and submit through Xcode.
AppImage allows you to create a single executable that works across most Linux distributions:
- Install required tools:
# Download AppImageKit
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x appimagetool- Create AppDir structure:
# Build with PyInstaller first
pyinstaller --name=p2i --windowed main.py
# Create AppDir structure
mkdir -p AppDir/usr/bin
cp -r dist/p2i/* AppDir/usr/bin/
# Add application icon
mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps/
cp icons/app_icon.png AppDir/usr/share/icons/hicolor/256x256/apps/p2i.png
# Create .desktop file
cat > AppDir/p2i.desktop << EOF
[Desktop Entry]
Name=p2i
Comment=PDF & Image Processing Tool
Exec=p2i
Icon=p2i
Type=Application
Categories=Office;Graphics;
EOF
# Create AppRun script
cat > AppDir/AppRun << EOF
#!/bin/bash
SELF=\$(readlink -f "\$0")
HERE=\${SELF%/*}
export PATH="\${HERE}/usr/bin:\${PATH}"
"\${HERE}/usr/bin/p2i" "\$@"
EOF
chmod +x AppDir/AppRun- Build the AppImage:
./appimagetool AppDir p2i-x86_64.AppImage- Install build dependencies:
sudo apt-get install python3-stdeb dh-python- Use
stdebto build a .deb package:
python setup.py --command-packages=stdeb.command bdist_deb- The .deb package will be created in the
deb_distdirectory.
- Install build dependencies:
sudo dnf install rpm-build- Build the RPM package:
python setup.py bdist_rpm- The .rpm package will be created in the
distdirectory.
- Create a
snapcraft.yamlfile:
name: p2i
version: '1.0.0'
summary: PDF & Image Processing Tool
description: |
p2i is a comprehensive GUI application for PDF and image operations,
including conversion, merging, splitting, compression, security, and more.
grade: stable
confinement: strict
base: core18
apps:
p2i:
command: bin/p2i
extensions: [gnome-3-28]
plugs:
- home
- removable-media
- network
- desktop
- desktop-legacy
- wayland
- x11
parts:
p2i:
plugin: python
python-version: python3
source: .
stage-packages:
- python3-tk
- python3-pil
- ghostscript
- poppler-utils- Build the snap package:
snapcraft- Create a GitHub repository for your project
- Go to the repository and click the "Releases" tab
- Click "Draft a new release"
- Set a tag version (e.g., "v1.0.0")
- Write release notes
- Upload your distribution packages:
- Windows: .exe installer or .zip with portable executable
- macOS: .dmg file or .zip with app bundle
- Linux: .AppImage, .deb, .rpm, and/or .snap files
- Publish the release
- Prepare your package:
python setup.py sdist bdist_wheel- Upload to PyPI:
twine upload dist/*- Users can then install with:
pip install p2i- Fork the conda-forge/staged-recipes repository
- Add a new recipe for your package
- Submit a pull request to get your package added to conda-forge
Set up automated builds using GitHub Actions:
- Create
.github/workflows/build.yml:
name: Build and Release
on:
push:
tags:
- 'v*'
jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build with PyInstaller
run: |
pyinstaller --name=p2i --windowed --onefile --icon=icons/app_icon.ico --add-data="icons;icons" main.py
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: p2i-windows
path: dist/p2i.exe
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build with PyInstaller
run: |
pyinstaller --name=p2i --windowed --icon=icons/app_icon.icns --add-data="icons:icons" main.py
- name: Create DMG
run: |
brew install create-dmg
create-dmg \
--volname "p2i Installer" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "p2i.app" 200 190 \
--hide-extension "p2i.app" \
--app-drop-link 600 185 \
"p2i-installer.dmg" \
"dist/p2i.app"
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: p2i-macos
path: p2i-installer.dmg
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build with PyInstaller
run: |
pyinstaller --name=p2i --windowed --add-data="icons:icons" main.py
- name: Create AppImage
run: |
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x appimagetool
mkdir -p AppDir/usr/bin
cp -r dist/p2i/* AppDir/usr/bin/
cp icons/app_icon.png AppDir/p2i.png
cat > AppDir/p2i.desktop << EOF
[Desktop Entry]
Name=p2i
Comment=PDF & Image Processing Tool
Exec=p2i
Icon=p2i
Type=Application
Categories=Office;Graphics;
EOF
cat > AppDir/AppRun << EOF
#!/bin/bash
SELF=\$(readlink -f "\$0")
HERE=\${SELF%/*}
export PATH="\${HERE}/usr/bin:\${PATH}"
"\${HERE}/usr/bin/p2i" "\$@"
EOF
chmod +x AppDir/AppRun
./appimagetool AppDir p2i-x86_64.AppImage
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: p2i-linux
path: p2i-x86_64.AppImage
release:
needs: [build-windows, build-macos, build-linux]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Download all artifacts
uses: actions/download-artifact@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: p2i ${{ github.ref }}
draft: false
prerelease: false
- name: Upload Windows Build
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./p2i-windows/p2i.exe
asset_name: p2i-windows-x64.exe
asset_content_type: application/octet-stream
- name: Upload macOS Build
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./p2i-macos/p2i-installer.dmg
asset_name: p2i-macos.dmg
asset_content_type: application/octet-stream
- name: Upload Linux Build
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./p2i-linux/p2i-x86_64.AppImage
asset_name: p2i-linux-x86_64.AppImage
asset_content_type: application/octet-stream- Version Numbering: Use semantic versioning (MAJOR.MINOR.PATCH)
- Update Version in Code:
- Update version in setup.py
- Update version in main.py or a dedicated version.py file
- Tag in Git: Create a git tag for each release
- Changelog: Maintain a CHANGELOG.md file
Example version.py file:
VERSION = '1.0.0'Example integrating version in main.py:
try:
from version import VERSION
except ImportError:
VERSION = '1.0.0'
def show_about():
"""Show about dialog"""
about_text = f"""p2i - PDF & Image Processing Tool
Version {VERSION}
A comprehensive toolkit for PDF and image processing.
...
"""
messagebox.showinfo("About p2i", about_text)To implement an auto-updater:
- Create an update server or use GitHub releases API
- Add code to check for updates on application startup
- If an update is available, prompt the user to download and install
Example update checker:
import requests
import json
import webbrowser
from version import VERSION
def check_for_updates():
try:
response = requests.get("https://api.github.com/repos/yourusername/p2i/releases/latest", timeout=5)
latest_release = json.loads(response.text)
latest_version = latest_release["tag_name"].lstrip("v")
if latest_version > VERSION:
# New version available
if messagebox.askyesno("Update Available",
f"A new version ({latest_version}) is available. Would you like to download it?"):
webbrowser.open(latest_release["html_url"])
except:
# Silently fail if update check fails
passTo collect anonymous usage statistics and crash reports:
- Add a privacy policy to your application
- Get user consent before collecting data
- Use a service like Sentry for crash reporting
def setup_crash_reporting():
if settings.get('enable_crash_reporting', False):
try:
import sentry_sdk
sentry_sdk.init(
"YOUR_SENTRY_DSN",
traces_sample_rate=0.1
)
except ImportError:
passFor commercial applications, implement a licensing system:
- Generate license keys with a secure algorithm
- Store and verify license keys locally
- Optionally validate licenses against a server
Remember to thoroughly test your packages before distribution, especially with dependencies that include compiled code, as they may behave differently across platforms.