update upload file
This commit is contained in:
parent
5c36fa0d3d
commit
4a5b6dd8aa
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
python -m uvicorn main:app --reload --host 127.0.0.1 --port 8443 --ssl-keyfile=key.pem --ssl-certfile=cert.pem
|
||||
Binary file not shown.
|
|
@ -1,101 +0,0 @@
|
|||
from fastapi import FastAPI, Body
|
||||
from pydantic import BaseModel
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from openai import OpenAI
|
||||
import pyautogui
|
||||
import os
|
||||
import time
|
||||
import pyperclip
|
||||
|
||||
print("Di chuot den o nhap tin nhan trong 5 giay ...")
|
||||
time.sleep(5)
|
||||
input_position = pyautogui.position()
|
||||
|
||||
print(f"Da lay toa do: {input_position}")
|
||||
|
||||
x,y = input_position
|
||||
|
||||
# Thiết lập API key cho OpenAI
|
||||
client = OpenAI(api_key="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") # Hoặc gán trực tiếp: "sk-..."
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Cấu hình CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Dữ liệu đầu vào cho API
|
||||
class Message(BaseModel):
|
||||
name: str
|
||||
message: str
|
||||
time: int
|
||||
room_id: str
|
||||
room_name: str
|
||||
x: int
|
||||
y: int
|
||||
|
||||
# Gọi OpenAI và giả lập nhập liệu
|
||||
def generate_and_type_reply(sender: str, content: str, x: int, y: int):
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": "Ban la tro ly tin nhan AI tra loi ngan gon va day du y, than thien. Tra loi tieng Anh. Format xuong dong bang \n"},
|
||||
{"role": "user", "content": f"{sender} sent: {content}"}
|
||||
]
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
print(f"AI tra loi: {reply}")
|
||||
|
||||
time.sleep(1)
|
||||
pyautogui.click(x, y)
|
||||
time.sleep(1)
|
||||
for line in reply.split("\n"):
|
||||
pyautogui.write(line, interval=0.01)
|
||||
pyautogui.hotkey("shift", "enter")
|
||||
time.sleep(1)
|
||||
time.sleep(0.5)
|
||||
pyautogui.press("enter")
|
||||
pyautogui.press("enter")
|
||||
pyautogui.press("enter")
|
||||
|
||||
return reply
|
||||
except Exception as e:
|
||||
return f"Lỗi khi gọi AI: {str(e)}"
|
||||
|
||||
# API để nhận tin nhắn và phản hồi
|
||||
@app.post("/reply/")
|
||||
def reply_to_message(msg: Message):
|
||||
print(f"[{msg.room_name}] {msg.name}: {msg.message}")
|
||||
reply = generate_and_type_reply(msg.name, msg.message, msg.x, msg.y)
|
||||
return {
|
||||
"sender": msg.name,
|
||||
"message": msg.message,
|
||||
"room": msg.room_name,
|
||||
"reply": reply
|
||||
}
|
||||
|
||||
@app.post("/type/")
|
||||
def type(message: str = Body(..., embed=True)):
|
||||
try:
|
||||
print(f"Typing message: ")
|
||||
|
||||
pyautogui.click(x, y)
|
||||
time.sleep(1)
|
||||
|
||||
# Copy vào clipboard và dán
|
||||
pyperclip.copy(message)
|
||||
pyautogui.hotkey("ctrl", "v")
|
||||
time.sleep(0.5)
|
||||
|
||||
# Gửi Enter để gửi
|
||||
pyautogui.press("enter")
|
||||
|
||||
return {"status": "success", "typed": message}
|
||||
except Exception as e:
|
||||
return {"status": "error", "detail": str(e)}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
fastapi==0.111.0
|
||||
uvicorn==0.29.0
|
||||
pydantic==2.7.1
|
||||
openai==1.30.1
|
||||
pyautogui==0.9.54
|
||||
pymsgbox==1.0.9
|
||||
pytweening==1.0.7
|
||||
pygetwindow==0.0.9
|
||||
pyrect==0.2.0
|
||||
PyScreeze==0.1.30
|
||||
pyobjc-core==10.1
|
||||
pyobjc-framework-Quartz==10.1
|
||||
pyobjc-framework-Cocoa==10.1
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID1TCCAr2gAwIBAgIUOWbfSNEe+a/qZo/1Y7DOFM6WZZ8wDQYJKoZIhvcNAQEL
|
||||
BQAwejELMAkGA1UEBhMCQVQxDjAMBgNVBAgMBUFkbWluMQ4wDAYDVQQHDAVBZG1p
|
||||
bjEOMAwGA1UECgwFQWRtaW4xCzAJBgNVBAsMAkFUMQ4wDAYDVQQDDAVBZG1pbjEe
|
||||
MBwGCSqGSIb3DQEJARYPYWRtaW5AZ21haWwuY29tMB4XDTI1MDgwNTAyMjMzOFoX
|
||||
DTI2MDgwNTAyMjMzOFowejELMAkGA1UEBhMCQVQxDjAMBgNVBAgMBUFkbWluMQ4w
|
||||
DAYDVQQHDAVBZG1pbjEOMAwGA1UECgwFQWRtaW4xCzAJBgNVBAsMAkFUMQ4wDAYD
|
||||
VQQDDAVBZG1pbjEeMBwGCSqGSIb3DQEJARYPYWRtaW5AZ21haWwuY29tMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtK8sAFtijD7IG5UP7DIE1zaGUUch
|
||||
YH9ubXOb0LX5tqb3asLVqFehveRUVolUoG3t+wM8yaxF/MBk4vkEZoEbhQiKGDfV
|
||||
37FakHcw74cLEPgMuVGU91/yt3ca8+6zSSYcsIpJaWIWFNlGMnk3TIs4HRnM80rY
|
||||
1R6q94SP3Yx2/DlcKoYxb/onnRs8pTJZPIw+YdRm1Yput6EO1WTjinG9JmAbeNsS
|
||||
f643nN0oQ5JYf0JTQXi/8iLJExwJwbl+97aS2+l/+tSujqIkLUE1ighxGk7ZxZfO
|
||||
3V9gd5kdSYbsMhQqDdEUjmmNqmAQ6F8MRkPu8+/jAfQH73+rzQBrqZMe+QIDAQAB
|
||||
o1MwUTAdBgNVHQ4EFgQUSmsyedYpQW1A9z0IVbQ4wR2gDcYwHwYDVR0jBBgwFoAU
|
||||
SmsyedYpQW1A9z0IVbQ4wR2gDcYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAZd0OO2r/vs+56PhDLnB9fMiA8WSD9d2J8VNaA1IYCRkEQwEHlFUQ
|
||||
CSu9ZIJtU+UXcDpnjzHc7hV1yYufhMTKUPWY9rlVypWoCoOyMw7Z9O0Tk+je4g2i
|
||||
ChV2HSAYFSAhJrt1zNUofNBjXjgX08Mr65LYo3eIj9Pl8dmfun5wjDAlNv6uj9+F
|
||||
eJzlsnCa2/fDn6zDGzKALXtNOPA/jNoKVP5uGbgCxaUNFA5dZvA5MBg8jXYC2r/Z
|
||||
bL47zml8alx+HOgxJkkeQqx5rjBw+j2fNK7h3ot6ME7Gu9+MDgg2n1rKiJqrWCTH
|
||||
oi0vQprP9ccgTHQB5cHTEQsOQhYcOjq9ug==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0rywAW2KMPsgb
|
||||
lQ/sMgTXNoZRRyFgf25tc5vQtfm2pvdqwtWoV6G95FRWiVSgbe37AzzJrEX8wGTi
|
||||
+QRmgRuFCIoYN9XfsVqQdzDvhwsQ+Ay5UZT3X/K3dxrz7rNJJhywiklpYhYU2UYy
|
||||
eTdMizgdGczzStjVHqr3hI/djHb8OVwqhjFv+iedGzylMlk8jD5h1GbVim63oQ7V
|
||||
ZOOKcb0mYBt42xJ/rjec3ShDklh/QlNBeL/yIskTHAnBuX73tpLb6X/61K6OoiQt
|
||||
QTWKCHEaTtnFl87dX2B3mR1JhuwyFCoN0RSOaY2qYBDoXwxGQ+7z7+MB9Afvf6vN
|
||||
AGupkx75AgMBAAECggEAAlrmqUxuCUf8gnyG+1QiRnfzcf3p+28JyANjegNtmvHl
|
||||
iqN5vRxQTPbCQSlm1c6Wm6JM2AMzqDunQC/QybFZqk2EhEdYO+NQFYI4nD4/vw1Z
|
||||
2Bs68USaEXlfIiPf1YTs4PTnR+hjab5OKSecK7zRHH/K6uoBHJNuFQFKGtzv95gv
|
||||
r0ECwJoZo+pWswYmUiHeaBLrxdAq4LNdKVLKcZ3Pi6NU3pSQynGRLkjTC1PqSiUm
|
||||
KAWHFEpt6Co5AVDkgBc5TNIex90khKZIRciFNL8VjR1su3EJZ2m71cqagvMhBVz1
|
||||
g4wHY7FR6liqiC9NuFT2FfVer56sX5+GZtw21J9lUQKBgQDm6avJ0NXplo7Lhta4
|
||||
4CI2nn1jdk+7kGVBCHyRevdtFrcXsO8rbpv8cyuZy+tNGtPg995pJYkE55uv11T1
|
||||
Ar8Qk0SbpMefexv33XcX5/wIGB34xgZ31XoUAYAkpFi7iY2hOQgpTaftOJ5D3UHj
|
||||
k9OHk+zRAclv8nvvrGY6KwENqQKBgQDIUIF0sRHQWbulSGOo2PeXO5p9Gh/brEg2
|
||||
/orfftj5BmY4Rmin6ZPeShh9huVxCwId3+DPZxYulyeu0BLLXloZ5Z8ZnDkiG9Ux
|
||||
okm1691BUqJTguKE3EuB5yZ6kOT8QL/9yifXYn+O8BDKN/V4s+nDop2e13GRrGoM
|
||||
SHS0jSo40QKBgC0IUy1XofAdIZoFoOiLJYD7Zb64+xG8NlL5gbmpfolyzaA8LFiP
|
||||
CLKyD0W+JBR0b+/Gx0RAQrmHJbCkUPg7YeGspsun1hp/GtQ7B5fM8TTu5yF2FRrX
|
||||
sigmpsP+nSNtzwYrJjFP6Lj1ur/HbashUDZ3nc6hB5TieFMkjwc3DcyJAoGBAK7m
|
||||
TAIdURaAIu0PpaSZvKSZcCxnEQX3CR6rZYn68WuNgNcF8v8ZXPir7XI8xDzNhc2b
|
||||
2mh888s+Q3HJT3+uJDGUYjQ0SVZUwvMRZhanmIoeookUMMAcsPj9YIWH2ce+qWPo
|
||||
jJzs7b9aMO6/qV74h9U/OSylpA2zYuzSnsO+tezhAoGBAIDenq6wO29gCbIdiOoU
|
||||
5AAKaIfHFSqr62eLjy/1ZhJ8rsmi75Du4MMwEsCJN/btx68SHnvdk1EL+sEJaUp1
|
||||
cUgX4inKRajuQtVQ5BFtH0/r5g5gXgCtlloz+1jyQi+i+YidW8tW+88kXCjO1ZiS
|
||||
JWLFknfFx8L1xUBW3R1Hadvz
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV="/Users/admin/Workspace/do-something/teams-bots/write-message/venv"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(venv) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(venv) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV "/Users/admin/Workspace/do-something/teams-bots/write-message/venv"
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = "(venv) $prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT "(venv) "
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/); you cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
# prevents error when using nested fish instances (Issue #93858)
|
||||
if functions -q _old_fish_prompt
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
end
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
set -e VIRTUAL_ENV_PROMPT
|
||||
if test "$argv[1]" != "nondestructive"
|
||||
# Self-destruct!
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV "/Users/admin/Workspace/do-something/teams-bots/write-message/venv"
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# fish uses a function instead of an env var to generate the prompt.
|
||||
|
||||
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
# With the original prompt function renamed, we can override with our own.
|
||||
function fish_prompt
|
||||
# Save the return status of the last command.
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal)
|
||||
|
||||
# Restore the return status of the previous command.
|
||||
echo "exit $old_status" | .
|
||||
# Output the original/"old" prompt.
|
||||
_old_fish_prompt
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
set -gx VIRTUAL_ENV_PROMPT "(venv) "
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from distro.distro import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from dotenv.__main__ import cli
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(cli())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from email_validator.__main__ import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from fastapi_cli.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from httpx import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from markdown_it.cli.parse import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from openai.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pygments.cmdline import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1 +0,0 @@
|
|||
python3
|
||||
|
|
@ -1 +0,0 @@
|
|||
/Users/admin/.pyenv/versions/3.10.13/bin/python3
|
||||
|
|
@ -1 +0,0 @@
|
|||
python3
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from tqdm.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from typer.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from uvicorn.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from watchfiles.cli import cli
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(cli())
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from websockets.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
Binary file not shown.
|
|
@ -1,159 +0,0 @@
|
|||
"""
|
||||
Python mapping for the AppKit framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import sys
|
||||
|
||||
import Foundation
|
||||
import objc
|
||||
from . import _metadata, _nsapp, _AppKit
|
||||
from ._inlines import _inline_list_
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="AppKit",
|
||||
frameworkIdentifier="com.apple.AppKit",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
"/System/Library/Frameworks/AppKit.framework"
|
||||
),
|
||||
globals_dict=globals(),
|
||||
inline_list=_inline_list_,
|
||||
parents=(
|
||||
_nsapp,
|
||||
_AppKit,
|
||||
Foundation,
|
||||
),
|
||||
metadict=_metadata.__dict__,
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
del sys.modules["AppKit._metadata"]
|
||||
|
||||
def fontdescriptor_get(self, key, default=None):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
return default
|
||||
return value
|
||||
|
||||
def fontdescriptor_getitem(self, key, default=None):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
return value
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSFontDescriptor",
|
||||
(("__getitem__", fontdescriptor_getitem), ("get", fontdescriptor_get)),
|
||||
)
|
||||
|
||||
# Fix types for a number of character constants
|
||||
# XXX: Move this to metadata
|
||||
globals_dict = globals()
|
||||
for nm in [
|
||||
"NSEnterCharacter",
|
||||
"NSBackspaceCharacter",
|
||||
"NSTabCharacter",
|
||||
"NSNewlineCharacter",
|
||||
"NSFormFeedCharacter",
|
||||
"NSCarriageReturnCharacter",
|
||||
"NSBackTabCharacter",
|
||||
"NSDeleteCharacter",
|
||||
"NSLineSeparatorCharacter",
|
||||
"NSParagraphSeparatorCharacter",
|
||||
"NSUpArrowFunctionKey",
|
||||
"NSDownArrowFunctionKey",
|
||||
"NSLeftArrowFunctionKey",
|
||||
"NSRightArrowFunctionKey",
|
||||
"NSF1FunctionKey",
|
||||
"NSF2FunctionKey",
|
||||
"NSF3FunctionKey",
|
||||
"NSF4FunctionKey",
|
||||
"NSF5FunctionKey",
|
||||
"NSF6FunctionKey",
|
||||
"NSF7FunctionKey",
|
||||
"NSF8FunctionKey",
|
||||
"NSF9FunctionKey",
|
||||
"NSF10FunctionKey",
|
||||
"NSF11FunctionKey",
|
||||
"NSF12FunctionKey",
|
||||
"NSF13FunctionKey",
|
||||
"NSF14FunctionKey",
|
||||
"NSF15FunctionKey",
|
||||
"NSF16FunctionKey",
|
||||
"NSF17FunctionKey",
|
||||
"NSF18FunctionKey",
|
||||
"NSF19FunctionKey",
|
||||
"NSF20FunctionKey",
|
||||
"NSF21FunctionKey",
|
||||
"NSF22FunctionKey",
|
||||
"NSF23FunctionKey",
|
||||
"NSF24FunctionKey",
|
||||
"NSF25FunctionKey",
|
||||
"NSF26FunctionKey",
|
||||
"NSF27FunctionKey",
|
||||
"NSF28FunctionKey",
|
||||
"NSF29FunctionKey",
|
||||
"NSF30FunctionKey",
|
||||
"NSF31FunctionKey",
|
||||
"NSF32FunctionKey",
|
||||
"NSF33FunctionKey",
|
||||
"NSF34FunctionKey",
|
||||
"NSF35FunctionKey",
|
||||
"NSInsertFunctionKey",
|
||||
"NSDeleteFunctionKey",
|
||||
"NSHomeFunctionKey",
|
||||
"NSBeginFunctionKey",
|
||||
"NSEndFunctionKey",
|
||||
"NSPageUpFunctionKey",
|
||||
"NSPageDownFunctionKey",
|
||||
"NSPrintScreenFunctionKey",
|
||||
"NSScrollLockFunctionKey",
|
||||
"NSPauseFunctionKey",
|
||||
"NSSysReqFunctionKey",
|
||||
"NSBreakFunctionKey",
|
||||
"NSResetFunctionKey",
|
||||
"NSStopFunctionKey",
|
||||
"NSMenuFunctionKey",
|
||||
"NSUserFunctionKey",
|
||||
"NSSystemFunctionKey",
|
||||
"NSPrintFunctionKey",
|
||||
"NSClearLineFunctionKey",
|
||||
"NSClearDisplayFunctionKey",
|
||||
"NSInsertLineFunctionKey",
|
||||
"NSDeleteLineFunctionKey",
|
||||
"NSInsertCharFunctionKey",
|
||||
"NSDeleteCharFunctionKey",
|
||||
"NSPrevFunctionKey",
|
||||
"NSNextFunctionKey",
|
||||
"NSSelectFunctionKey",
|
||||
"NSExecuteFunctionKey",
|
||||
"NSUndoFunctionKey",
|
||||
"NSRedoFunctionKey",
|
||||
"NSFindFunctionKey",
|
||||
"NSHelpFunctionKey",
|
||||
"NSModeSwitchFunctionKey",
|
||||
]:
|
||||
try:
|
||||
globals_dict[nm] = chr(__getattr__(nm)) # noqa: F821
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
|
||||
|
||||
def NSDictionaryOfVariableBindings(*names):
|
||||
"""
|
||||
Return a dictionary with the given names and there values.
|
||||
"""
|
||||
import sys
|
||||
|
||||
variables = sys._getframe(1).f_locals
|
||||
|
||||
return {nm: variables[nm] for nm in names}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -1,29 +0,0 @@
|
|||
import objc as _objc
|
||||
import AppKit as _AppKit
|
||||
|
||||
|
||||
class _NSApp:
|
||||
"""
|
||||
Helper class to emulate NSApp in Python.
|
||||
"""
|
||||
|
||||
def __getrealapp(self):
|
||||
d = {}
|
||||
_objc.loadBundleVariables(_AppKit.__bundle__, d, [("NSApp", b"@")])
|
||||
return d.get("NSApp")
|
||||
|
||||
__class__ = property(lambda self: self.__getrealapp().__class__)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.__getrealapp(), name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
return setattr(self.__getrealapp(), name, value)
|
||||
|
||||
def __call__(self):
|
||||
# Compatibility with previous versions.
|
||||
return self.__getrealapp()
|
||||
|
||||
|
||||
NSApp = _NSApp()
|
||||
del _NSApp
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
"""
|
||||
Python mapping for the Cocoa framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import AppKit
|
||||
import objc
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="Cocoa",
|
||||
frameworkIdentifier=None,
|
||||
frameworkPath=None,
|
||||
globals_dict=globals(),
|
||||
inline_list=None,
|
||||
parents=(AppKit,),
|
||||
metadict={},
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,37 +0,0 @@
|
|||
"""
|
||||
Python mapping for the CoreFoundation framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import sys
|
||||
|
||||
import objc
|
||||
from . import _metadata, _CoreFoundation, _static
|
||||
from ._inlines import _inline_list_
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="CoreFoundation",
|
||||
frameworkIdentifier="com.apple.CoreFoundation",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
"/System/Library/Frameworks/CoreFoundation.framework"
|
||||
),
|
||||
globals_dict=globals(),
|
||||
inline_list=_inline_list_,
|
||||
parents=(
|
||||
_CoreFoundation,
|
||||
_static,
|
||||
),
|
||||
metadict=_metadata.__dict__,
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
del sys.modules["CoreFoundation._metadata"]
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -1,107 +0,0 @@
|
|||
import CoreFoundation as _CF
|
||||
import objc as _objc
|
||||
|
||||
|
||||
#
|
||||
# 'Emulation' for CFArray constructors
|
||||
#
|
||||
def _setup():
|
||||
NSArray = _objc.lookUpClass("NSArray")
|
||||
NSMutableArray = _objc.lookUpClass("NSMutableArray")
|
||||
|
||||
def CFArrayCreate(allocator, values, numvalues, callbacks):
|
||||
assert callbacks is None
|
||||
return NSArray.alloc().initWithArray_(values[:numvalues])
|
||||
|
||||
def CFArrayCreateMutable(allocator, capacity, callbacks):
|
||||
assert callbacks is None
|
||||
return NSMutableArray.alloc().init()
|
||||
|
||||
return CFArrayCreate, CFArrayCreateMutable
|
||||
|
||||
|
||||
CFArrayCreate, CFArrayCreateMutable = _setup()
|
||||
|
||||
# CFDictionary emulation functions
|
||||
|
||||
|
||||
def _setup():
|
||||
NSDictionary = _objc.lookUpClass("NSDictionary")
|
||||
NSMutableDictionary = _objc.lookUpClass("NSMutableDictionary")
|
||||
|
||||
def CFDictionaryCreate(
|
||||
allocator, keys, values, numValues, keyCallbacks, valueCallbacks
|
||||
):
|
||||
assert keyCallbacks is None
|
||||
assert valueCallbacks is None
|
||||
|
||||
keys = list(keys)[:numValues]
|
||||
values = list(values)[:numValues]
|
||||
|
||||
return NSDictionary.dictionaryWithDictionary_(dict(zip(keys, values)))
|
||||
|
||||
def CFDictionaryCreateMutable(allocator, capacity, keyCallbacks, valueCallbacks):
|
||||
assert keyCallbacks is None
|
||||
assert valueCallbacks is None
|
||||
|
||||
return NSMutableDictionary.dictionary()
|
||||
|
||||
return CFDictionaryCreate, CFDictionaryCreateMutable
|
||||
|
||||
|
||||
CFDictionaryCreate, CFDictionaryCreateMutable = _setup()
|
||||
|
||||
|
||||
# CFSet emulation functions
|
||||
|
||||
|
||||
def _setup():
|
||||
NSSet = _objc.lookUpClass("NSSet")
|
||||
NSMutableSet = _objc.lookUpClass("NSMutableSet")
|
||||
|
||||
def CFSetCreate(allocator, values, numvalues, callbacks):
|
||||
assert callbacks is None
|
||||
return NSSet.alloc().initWithArray_(values[:numvalues])
|
||||
|
||||
def CFSetCreateMutable(allocator, capacity, callbacks):
|
||||
assert callbacks is None
|
||||
return NSMutableSet.alloc().init()
|
||||
|
||||
return CFSetCreate, CFSetCreateMutable
|
||||
|
||||
|
||||
CFSetCreate, CFSetCreateMutable = _setup()
|
||||
|
||||
kCFTypeArrayCallBacks = None
|
||||
kCFTypeDictionaryKeyCallBacks = None
|
||||
kCFTypeDictionaryValueCallBacks = None
|
||||
kCFTypeSetCallBacks = None
|
||||
|
||||
|
||||
#
|
||||
# Implementation of a number of macro's in the CFBundle API
|
||||
#
|
||||
|
||||
|
||||
def CFCopyLocalizedString(key, comment):
|
||||
return _CF.CFBundleCopyLocalizedString(
|
||||
_CF.CFBundleGetMainBundle(), (key), (key), None
|
||||
)
|
||||
|
||||
|
||||
def CFCopyLocalizedStringFromTable(key, tbl, comment):
|
||||
return _CF.CFBundleCopyLocalizedString(
|
||||
_CF.CFBundleGetMainBundle(), (key), (key), (tbl)
|
||||
)
|
||||
|
||||
|
||||
def CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment):
|
||||
return _CF.CFBundleCopyLocalizedString((bundle), (key), (key), (tbl))
|
||||
|
||||
|
||||
def CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment):
|
||||
return _CF.CFBundleCopyLocalizedString((bundle), (key), (value), (tbl))
|
||||
|
||||
|
||||
def CFSTR(strval):
|
||||
return _objc.lookUpClass("NSString").stringWithString_(strval)
|
||||
Binary file not shown.
|
|
@ -1,196 +0,0 @@
|
|||
"""
|
||||
Python mapping for the Foundation framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import sys
|
||||
|
||||
import CoreFoundation
|
||||
import objc
|
||||
from . import _Foundation, _metadata, _functiondefines, _context
|
||||
from ._inlines import _inline_list_
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="Foundation",
|
||||
frameworkIdentifier="com.apple.Foundation",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
"/System/Library/Frameworks/Foundation.framework"
|
||||
),
|
||||
globals_dict=globals(),
|
||||
inline_list=_inline_list_,
|
||||
parents=(
|
||||
_Foundation,
|
||||
_functiondefines,
|
||||
_context,
|
||||
CoreFoundation,
|
||||
),
|
||||
metadict=_metadata.__dict__,
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
del sys.modules["Foundation._metadata"]
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSAttributedString", (("__len__", lambda self: self.length()),)
|
||||
)
|
||||
|
||||
objc.addConvenienceForBasicMapping("NSMergeConflict", True)
|
||||
objc.addConvenienceForBasicMapping("NSUbiquitousKeyValueStore", False)
|
||||
objc.addConvenienceForBasicMapping("NSUserDefaults", False)
|
||||
|
||||
NSNull = objc.lookUpClass("NSNull")
|
||||
|
||||
def nscache_getitem(self, key):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
|
||||
elif value is NSNull.null():
|
||||
return None
|
||||
|
||||
else:
|
||||
return value
|
||||
|
||||
def nscache_get(self, key, default=None):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
return default
|
||||
elif value is NSNull.null():
|
||||
return None
|
||||
return value
|
||||
|
||||
def nscache_setitem(self, key, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
self.setObject_forKey_(value, key)
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSCache",
|
||||
(
|
||||
("__getitem__", nscache_getitem),
|
||||
("get", nscache_get),
|
||||
("__setitem__", nscache_setitem),
|
||||
("__delitem__", lambda self, key: self.removeObjectForKey_(key)),
|
||||
("clear", lambda self: self.removeAllObjects()),
|
||||
),
|
||||
)
|
||||
|
||||
def hash_add(self, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
self.addObject_(value)
|
||||
|
||||
def hash_contains(self, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
return self.containsObject_(value)
|
||||
|
||||
def hash_remove(self, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
self.removeObject_(value)
|
||||
|
||||
def hash_pop(self):
|
||||
value = self.anyObject()
|
||||
self.removeObject_(value)
|
||||
if value is NSNull.null():
|
||||
return None
|
||||
else:
|
||||
return value
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSHashTable",
|
||||
(
|
||||
("__len__", lambda self: self.count()),
|
||||
("clear", lambda self: self.removeAllObjects()),
|
||||
("__iter__", lambda self: iter(self.objectEnumerator())),
|
||||
("add", hash_add),
|
||||
("remove", hash_remove),
|
||||
("__contains__", hash_contains),
|
||||
("pop", hash_pop),
|
||||
),
|
||||
)
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSIndexPath", (("__len__", lambda self: self.count()),)
|
||||
)
|
||||
|
||||
if sys.maxsize > 2**32:
|
||||
NSNotFound = 0x7FFFFFFFFFFFFFFF
|
||||
else:
|
||||
NSNotFound = 0x7FFFFFFF
|
||||
|
||||
def indexset_iter(self):
|
||||
value = self.firstIndex()
|
||||
while value != NSNotFound:
|
||||
yield value
|
||||
value = self.indexGreaterThanIndex_(value)
|
||||
|
||||
def indexset_reversed(self):
|
||||
value = self.lastIndex()
|
||||
while value != NSNotFound:
|
||||
yield value
|
||||
value = self.indexLessThanIndex_(value)
|
||||
|
||||
NSIndexSet = objc.lookUpClass("NSIndexSet")
|
||||
|
||||
def indexset_eq(self, other):
|
||||
if not isinstance(other, NSIndexSet):
|
||||
return False
|
||||
|
||||
return self.isEqualToIndexSet_(other)
|
||||
|
||||
def indexset_ne(self, other):
|
||||
if not isinstance(other, NSIndexSet):
|
||||
return True
|
||||
|
||||
return not self.isEqualToIndexSet_(other)
|
||||
|
||||
def indexset_contains(self, value):
|
||||
try:
|
||||
return self.containsIndex_(value)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSIndexSet",
|
||||
(
|
||||
("__len__", lambda self: self.count()),
|
||||
("__iter__", indexset_iter),
|
||||
("__reversed__", indexset_reversed),
|
||||
("__eq__", indexset_eq),
|
||||
("__ne__", indexset_ne),
|
||||
("__contains__", indexset_contains),
|
||||
),
|
||||
)
|
||||
|
||||
# Add 'update', '-=', '+='
|
||||
objc.addConvenienceForClass(
|
||||
"NSMutableIndexSet",
|
||||
(
|
||||
("clear", lambda self: self.removeAllIndexes()),
|
||||
("add", lambda self, value: self.addIndex_(value)),
|
||||
("remove", lambda self, value: self.removeIndex_(value)),
|
||||
),
|
||||
)
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSLocale", (("__getitem__", lambda self, key: self.objectForKey_(key)),)
|
||||
)
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
|
||||
from objc import NSDecimal, YES, NO # isort:skip # noqa: E402, F401
|
||||
|
||||
import Foundation._context # isort:skip # noqa: E402
|
||||
import Foundation._functiondefines # isort:skip # noqa: E402
|
||||
import Foundation._nsindexset # isort:skip # noqa: E402
|
||||
import Foundation._nsobject # isort:skip # noqa: E402, F401
|
||||
import Foundation._nsurl # isort:skip # noqa: E402, F401
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,26 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
|
||||
class NSDisabledAutomaticTermination:
|
||||
def __init__(self, reason):
|
||||
self._reason = reason
|
||||
self._info = Foundation.NSProcessInfo.processInfo()
|
||||
|
||||
def __enter__(self):
|
||||
self._info.disableAutomaticTermination_(self._reason)
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._info.enableAutomaticTermination_(self._reason)
|
||||
return False
|
||||
|
||||
|
||||
class NSDisabledSuddenTermination:
|
||||
def __init__(self):
|
||||
self._info = Foundation.NSProcessInfo.processInfo()
|
||||
|
||||
def __enter__(self):
|
||||
self._info.disableSuddenTermination()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._info.enableSuddenTermination()
|
||||
return False
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
"""
|
||||
Port of "function defines".
|
||||
"""
|
||||
import Foundation as _Foundation
|
||||
|
||||
|
||||
def NSLocalizedString(key, comment):
|
||||
return _Foundation.NSBundle.mainBundle().localizedStringForKey_value_table_(
|
||||
key, "", None
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedStringFromTable(key, tbl, comment):
|
||||
return _Foundation.NSBundle.mainBundle().localizedStringForKey_value_table_(
|
||||
key, "", tbl
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment):
|
||||
return bundle.localizedStringForKey_value_table_(key, "", tbl)
|
||||
|
||||
|
||||
def NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment):
|
||||
return bundle.localizedStringForKey_value_table_(key, val, tbl)
|
||||
|
||||
|
||||
def NSLocalizedAttributedString(key, comment):
|
||||
return (
|
||||
_Foundation.NSBundle.mainBundle().localizedAttributedStringForKey_value_table_(
|
||||
key, "", None
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedAttributedStringFromTable(key, tbl, comment):
|
||||
return _Foundation.NSBundle.mainBundle.localizedAttributedStringForKey_value_table_(
|
||||
key, "", tbl
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedAttributedStringFromTableInBundle(key, tbl, bundle, comment):
|
||||
return bundle.localizedAttributedStringForKey_value_table_(key, "", tbl)
|
||||
|
||||
|
||||
def NSLocalizedAttributedStringWithDefaultValue(key, tbl, bundle, val, comment):
|
||||
return bundle.localizedAttributedStringForKey_value_table_(key, val, tbl)
|
||||
|
||||
|
||||
def MIN(a, b):
|
||||
if a < b:
|
||||
return a
|
||||
else:
|
||||
return b
|
||||
|
||||
|
||||
def MAX(a, b):
|
||||
if a < b:
|
||||
return b
|
||||
else:
|
||||
return a
|
||||
|
||||
|
||||
ABS = abs
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -1,21 +0,0 @@
|
|||
import objc
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.length()
|
||||
|
||||
|
||||
def __getitem__(self, idx):
|
||||
if isinstance(idx, slice):
|
||||
raise ValueError(idx)
|
||||
return self.indexAtPosition_(idx)
|
||||
|
||||
|
||||
def __add__(self, value):
|
||||
return self.indexPathByAddingIndex_(value)
|
||||
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSIndexPath",
|
||||
(("__len__", __len__), ("__getitem__", __getitem__), ("__add__", __add__)),
|
||||
)
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
"""
|
||||
Define a category on NSObject with some useful methods.
|
||||
"""
|
||||
import sys
|
||||
|
||||
import objc
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
|
||||
def _str(v):
|
||||
return v
|
||||
|
||||
exec(
|
||||
"""\
|
||||
def _raise(exc_type, exc_value, exc_trace):
|
||||
raise exc_type, exc_value, exc_trace
|
||||
"""
|
||||
)
|
||||
else:
|
||||
|
||||
def _str(v):
|
||||
if isinstance(v, str):
|
||||
return v
|
||||
return v.decode("ascii")
|
||||
|
||||
def _raise(exc_type, exc_value, exc_trace):
|
||||
raise exc_type(exc_value).with_traceback(exc_trace)
|
||||
|
||||
|
||||
NSObject = objc.lookUpClass("NSObject")
|
||||
|
||||
|
||||
class NSObject(objc.Category(NSObject)):
|
||||
@objc.namedSelector(b"_pyobjc_performOnThread:")
|
||||
def _pyobjc_performOnThread_(self, callinfo):
|
||||
try:
|
||||
sel, arg = callinfo
|
||||
m = getattr(self, _str(sel))
|
||||
m(arg)
|
||||
except: # noqa: E722, B001
|
||||
import traceback
|
||||
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
@objc.namedSelector(b"_pyobjc_performOnThreadWithResult:")
|
||||
def _pyobjc_performOnThreadWithResult_(self, callinfo):
|
||||
try:
|
||||
sel, arg, result = callinfo
|
||||
m = getattr(self, _str(sel))
|
||||
r = m(arg)
|
||||
result.append((True, r))
|
||||
except: # noqa: E722, B001
|
||||
result.append((False, sys.exc_info()))
|
||||
|
||||
if hasattr(NSObject, "performSelector_onThread_withObject_waitUntilDone_"):
|
||||
|
||||
@objc.namedSelector(
|
||||
b"pyobjc_performSelector:onThread:withObject:waitUntilDone:"
|
||||
)
|
||||
def pyobjc_performSelector_onThread_withObject_waitUntilDone_(
|
||||
self, aSelector, thread, arg, wait
|
||||
):
|
||||
"""
|
||||
A version of performSelector:onThread:withObject:waitUntilDone: that
|
||||
will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop on the other thread).
|
||||
"""
|
||||
self.performSelector_onThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThread:", thread, (aSelector, arg), wait
|
||||
)
|
||||
|
||||
@objc.namedSelector(
|
||||
b"pyobjc_performSelector:onThread:withObject:waitUntilDone:modes:"
|
||||
)
|
||||
def pyobjc_performSelector_onThread_withObject_waitUntilDone_modes_(
|
||||
self, aSelector, thread, arg, wait, modes
|
||||
):
|
||||
"""
|
||||
A version of performSelector:onThread:withObject:waitUntilDone:modes:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop on the other thread).
|
||||
"""
|
||||
self.performSelector_onThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThread:", thread, (aSelector, arg), wait, modes
|
||||
)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:withObject:afterDelay:")
|
||||
def pyobjc_performSelector_withObject_afterDelay_(self, aSelector, arg, delay):
|
||||
"""
|
||||
A version of performSelector:withObject:afterDelay:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop).
|
||||
"""
|
||||
self.performSelector_withObject_afterDelay_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), delay
|
||||
)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:withObject:afterDelay:inModes:")
|
||||
def pyobjc_performSelector_withObject_afterDelay_inModes_(
|
||||
self, aSelector, arg, delay, modes
|
||||
):
|
||||
"""
|
||||
A version of performSelector:withObject:afterDelay:inModes:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop).
|
||||
"""
|
||||
self.performSelector_withObject_afterDelay_inModes_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), delay, modes
|
||||
)
|
||||
|
||||
if hasattr(NSObject, "performSelectorInBackground_withObject_"):
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorInBackground:withObject:")
|
||||
def pyobjc_performSelectorInBackground_withObject_(self, aSelector, arg):
|
||||
"""
|
||||
A version of performSelectorInBackground:withObject:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop).
|
||||
"""
|
||||
self.performSelectorInBackground_withObject_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg)
|
||||
)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:waitUntilDone:")
|
||||
def pyobjc_performSelectorOnMainThread_withObject_waitUntilDone_(
|
||||
self, aSelector, arg, wait
|
||||
):
|
||||
"""
|
||||
A version of performSelectorOnMainThread:withObject:waitUntilDone:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop in the main thread).
|
||||
"""
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), wait
|
||||
)
|
||||
|
||||
@objc.namedSelector(
|
||||
b"pyobjc_performSelectorOnMainThread:withObject:waitUntilDone:modes:"
|
||||
)
|
||||
def pyobjc_performSelectorOnMainThread_withObject_waitUntilDone_modes_(
|
||||
self, aSelector, arg, wait, modes
|
||||
):
|
||||
"""
|
||||
A version of performSelectorOnMainThread:withObject:waitUntilDone:modes:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop in the main thread).
|
||||
"""
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), wait, modes
|
||||
)
|
||||
|
||||
# And some a some versions that return results
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:modes:")
|
||||
def pyobjc_performSelectorOnMainThread_withObject_modes_(
|
||||
self, aSelector, arg, modes
|
||||
):
|
||||
"""
|
||||
Simular to performSelectorOnMainThread:withObject:waitUntilDone:modes:,
|
||||
but:
|
||||
|
||||
- always waits until done
|
||||
- returns the return value of the called method
|
||||
- if the called method raises an exception, this will raise the same
|
||||
exception
|
||||
"""
|
||||
result = []
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThreadWithResult:", (aSelector, arg, result), True, modes
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:")
|
||||
def pyobjc_performSelectorOnMainThread_withObject_(self, aSelector, arg):
|
||||
result = []
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThreadWithResult:", (aSelector, arg, result), True
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
if hasattr(NSObject, "performSelector_onThread_withObject_waitUntilDone_"):
|
||||
# These methods require Leopard, don't define them if the
|
||||
# platform functionality isn't present.
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:modes:")
|
||||
def pyobjc_performSelector_onThread_withObject_modes_(
|
||||
self, aSelector, thread, arg, modes
|
||||
):
|
||||
result = []
|
||||
self.performSelector_onThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThreadWithResult:",
|
||||
thread,
|
||||
(aSelector, arg, result),
|
||||
True,
|
||||
modes,
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:")
|
||||
def pyobjc_performSelector_onThread_withObject_(self, aSelector, thread, arg):
|
||||
result = []
|
||||
self.performSelector_onThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThreadWithResult:",
|
||||
thread,
|
||||
(aSelector, arg, result),
|
||||
True,
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
|
||||
del NSObject
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
"""
|
||||
Helpers for NSURL
|
||||
"""
|
||||
import sys
|
||||
import objc
|
||||
|
||||
|
||||
def __fspath__(self):
|
||||
if self.scheme() == "file":
|
||||
# self.fileSystemRepresentation returns a byte string,
|
||||
# whereas most user code expects regular strings. Decode
|
||||
# in the same way as extension functions in the ``os`` module.
|
||||
return self.fileSystemRepresentation().decode(
|
||||
sys.getfilesystemencoding(), sys.getfilesystemencodeerrors()
|
||||
)
|
||||
|
||||
raise TypeError(f"NSURL with scheme {self.scheme()!r} instead of 'file'")
|
||||
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSURL",
|
||||
(("__fspath__", __fspath__),),
|
||||
)
|
||||
|
|
@ -1 +0,0 @@
|
|||
pip
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
Copyright 2010 Pallets
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: MarkupSafe
|
||||
Version: 3.0.2
|
||||
Summary: Safely add untrusted strings to HTML/XML markup.
|
||||
Maintainer-email: Pallets <contact@palletsprojects.com>
|
||||
License: Copyright 2010 Pallets
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Project-URL: Donate, https://palletsprojects.com/donate
|
||||
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
|
||||
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
|
||||
Project-URL: Source, https://github.com/pallets/markupsafe/
|
||||
Project-URL: Chat, https://discord.gg/pallets
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
||||
Classifier: Typing :: Typed
|
||||
Requires-Python: >=3.9
|
||||
Description-Content-Type: text/markdown
|
||||
License-File: LICENSE.txt
|
||||
|
||||
# MarkupSafe
|
||||
|
||||
MarkupSafe implements a text object that escapes characters so it is
|
||||
safe to use in HTML and XML. Characters that have special meanings are
|
||||
replaced so that they display as the actual characters. This mitigates
|
||||
injection attacks, meaning untrusted user input can safely be displayed
|
||||
on a page.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```pycon
|
||||
>>> from markupsafe import Markup, escape
|
||||
|
||||
>>> # escape replaces special characters and wraps in Markup
|
||||
>>> escape("<script>alert(document.cookie);</script>")
|
||||
Markup('<script>alert(document.cookie);</script>')
|
||||
|
||||
>>> # wrap in Markup to mark text "safe" and prevent escaping
|
||||
>>> Markup("<strong>Hello</strong>")
|
||||
Markup('<strong>hello</strong>')
|
||||
|
||||
>>> escape(Markup("<strong>Hello</strong>"))
|
||||
Markup('<strong>hello</strong>')
|
||||
|
||||
>>> # Markup is a str subclass
|
||||
>>> # methods and operators escape their arguments
|
||||
>>> template = Markup("Hello <em>{name}</em>")
|
||||
>>> template.format(name='"World"')
|
||||
Markup('Hello <em>"World"</em>')
|
||||
```
|
||||
|
||||
## Donate
|
||||
|
||||
The Pallets organization develops and supports MarkupSafe and other
|
||||
popular packages. In order to grow the community of contributors and
|
||||
users, and allow the maintainers to devote more time to the projects,
|
||||
[please donate today][].
|
||||
|
||||
[please donate today]: https://palletsprojects.com/donate
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
|
||||
MarkupSafe-3.0.2.dist-info/METADATA,sha256=aAwbZhSmXdfFuMM-rEHpeiHRkBOGESyVLJIuwzHP-nw,3975
|
||||
MarkupSafe-3.0.2.dist-info/RECORD,,
|
||||
MarkupSafe-3.0.2.dist-info/WHEEL,sha256=lXrF9eVJm7UF3ZOBaBu2Y-RekBGubHbC1Bvbd4BEjAQ,109
|
||||
MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
|
||||
markupsafe/__init__.py,sha256=sr-U6_27DfaSrj5jnHYxWN-pvhM27sjlDplMDPZKm7k,13214
|
||||
markupsafe/__pycache__/__init__.cpython-310.pyc,,
|
||||
markupsafe/__pycache__/_native.cpython-310.pyc,,
|
||||
markupsafe/_native.py,sha256=hSLs8Jmz5aqayuengJJ3kdT5PwNpBWpKrmQSdipndC8,210
|
||||
markupsafe/_speedups.c,sha256=O7XulmTo-epI6n2FtMVOrJXl8EAaIwD2iNYmBI5SEoQ,4149
|
||||
markupsafe/_speedups.cpython-310-darwin.so,sha256=3f9Fj2FHr4Ue7g2ODjkMpKlhPZCbjE0h7ZPvY4L8aUs,50688
|
||||
markupsafe/_speedups.pyi,sha256=ENd1bYe7gbBUf2ywyYWOGUpnXOHNJ-cgTNqetlW8h5k,41
|
||||
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: setuptools (75.2.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp310-cp310-macosx_11_0_arm64
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
markupsafe
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: MouseInfo
|
||||
Version: 0.1.3
|
||||
Summary: An application to display XY position and RGB color information for the pixel currently under the mouse. Works on Python 2 and 3.
|
||||
Home-page: https://github.com/asweigart/mouseinfo
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: GPLv3+
|
||||
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Description-Content-Type: text/markdown
|
||||
|
||||
MouseInfo
|
||||
======
|
||||
|
||||
An application to display XY position and RGB color information for the pixel currently under the mouse. Works on Python 2 and 3. This is useful for GUI automation planning.
|
||||
|
||||
The full documentation is at https://mouseinfo.readthedocs.io/en/latest/
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install with pip, run:
|
||||
|
||||
pip install mouseinfo
|
||||
|
||||
Quickstart Guide
|
||||
----------------
|
||||
|
||||
To run this application, enter the following into the terminal:
|
||||
|
||||
python3 -m mouseinfo
|
||||
|
||||
Or for Python 2, run:
|
||||
|
||||
python -m mouseinfo
|
||||
|
||||
Alternatively, to run it from the interactive shell or a Python program:
|
||||
|
||||
>>> import mouseinfo
|
||||
>>> mouseinfo.mouseInfo()
|
||||
|
||||
The Mouse Info application displays the current XY coordinates of the mouse cursor, as well as the RGB color information of the pixel directly under the cursor. This can be useful for planning out GUI automation tests where the mouse is controlled by a script (such as a Python script with PyAutoGUI) to click on the screen at specific coordinates.
|
||||
|
||||
The "Copy" buttons will copy this mouse information to the clipboard, while the "Log" buttons will add this mouse information to the text field in the application. The RGB color information is given as a comman-delimited, three-integer red, green, and blue values as decimals from 0 to 255. The hex values of the RGB value is also given.
|
||||
|
||||
For practical use, you should set the keyboard focus on these buttons by tabbing over them. This leaves you free to move the mouse into position and then press space or Enter to log the current mouse coordinates/RGB value.
|
||||
|
||||
The contents of the log text field can be saved by clicking "Save Log". This will automatically overwrite any file with the provided name. A screenshot can also be saved by clicking "Save Screenshot"
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
If you'd like to contribute to MouseInfo, check out https://github.com/asweigart/mouseinfo
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
MANIFEST.in
|
||||
README.md
|
||||
setup.cfg
|
||||
setup.py
|
||||
src/MouseInfo.egg-info/PKG-INFO
|
||||
src/MouseInfo.egg-info/SOURCES.txt
|
||||
src/MouseInfo.egg-info/dependency_links.txt
|
||||
src/MouseInfo.egg-info/requires.txt
|
||||
src/MouseInfo.egg-info/top_level.txt
|
||||
src/mouseinfo/__init__.py
|
||||
src/mouseinfo/__main__.py
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
../mouseinfo/__init__.py
|
||||
../mouseinfo/__main__.py
|
||||
../mouseinfo/__pycache__/__init__.cpython-310.pyc
|
||||
../mouseinfo/__pycache__/__main__.cpython-310.pyc
|
||||
PKG-INFO
|
||||
SOURCES.txt
|
||||
dependency_links.txt
|
||||
requires.txt
|
||||
top_level.txt
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
pyperclip
|
||||
|
||||
[:platform_system == "Darwin"]
|
||||
rubicon-objc
|
||||
|
||||
[:platform_system == "Linux" and python_version < "3.0"]
|
||||
Xlib
|
||||
|
||||
[:platform_system == "Linux" and python_version >= "3.0"]
|
||||
python3-Xlib
|
||||
|
||||
[:python_version == "2.7"]
|
||||
Pillow>=2.0.0
|
||||
|
||||
[:python_version == "3.2"]
|
||||
Pillow<=3.4.2,>=2.0.0
|
||||
|
||||
[:python_version == "3.3"]
|
||||
Pillow<=4.3.0,>=2.0.0
|
||||
|
||||
[:python_version == "3.4"]
|
||||
Pillow<=5.4.1,>=2.5.0
|
||||
|
||||
[:python_version == "3.5"]
|
||||
Pillow>=3.2.0
|
||||
|
||||
[:python_version == "3.6"]
|
||||
Pillow>=4.0.0
|
||||
|
||||
[:python_version == "3.7"]
|
||||
Pillow>=5.2.0
|
||||
|
|
@ -1 +0,0 @@
|
|||
mouseinfo
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,291 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from io import BytesIO
|
||||
from typing import IO
|
||||
|
||||
from . import ExifTags, Image, ImageFile
|
||||
|
||||
try:
|
||||
from . import _avif
|
||||
|
||||
SUPPORTED = True
|
||||
except ImportError:
|
||||
SUPPORTED = False
|
||||
|
||||
# Decoder options as module globals, until there is a way to pass parameters
|
||||
# to Image.open (see https://github.com/python-pillow/Pillow/issues/569)
|
||||
DECODE_CODEC_CHOICE = "auto"
|
||||
DEFAULT_MAX_THREADS = 0
|
||||
|
||||
|
||||
def get_codec_version(codec_name: str) -> str | None:
|
||||
versions = _avif.codec_versions()
|
||||
for version in versions.split(", "):
|
||||
if version.split(" [")[0] == codec_name:
|
||||
return version.split(":")[-1].split(" ")[0]
|
||||
return None
|
||||
|
||||
|
||||
def _accept(prefix: bytes) -> bool | str:
|
||||
if prefix[4:8] != b"ftyp":
|
||||
return False
|
||||
major_brand = prefix[8:12]
|
||||
if major_brand in (
|
||||
# coding brands
|
||||
b"avif",
|
||||
b"avis",
|
||||
# We accept files with AVIF container brands; we can't yet know if
|
||||
# the ftyp box has the correct compatible brands, but if it doesn't
|
||||
# then the plugin will raise a SyntaxError which Pillow will catch
|
||||
# before moving on to the next plugin that accepts the file.
|
||||
#
|
||||
# Also, because this file might not actually be an AVIF file, we
|
||||
# don't raise an error if AVIF support isn't properly compiled.
|
||||
b"mif1",
|
||||
b"msf1",
|
||||
):
|
||||
if not SUPPORTED:
|
||||
return (
|
||||
"image file could not be identified because AVIF support not installed"
|
||||
)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _get_default_max_threads() -> int:
|
||||
if DEFAULT_MAX_THREADS:
|
||||
return DEFAULT_MAX_THREADS
|
||||
if hasattr(os, "sched_getaffinity"):
|
||||
return len(os.sched_getaffinity(0))
|
||||
else:
|
||||
return os.cpu_count() or 1
|
||||
|
||||
|
||||
class AvifImageFile(ImageFile.ImageFile):
|
||||
format = "AVIF"
|
||||
format_description = "AVIF image"
|
||||
__frame = -1
|
||||
|
||||
def _open(self) -> None:
|
||||
if not SUPPORTED:
|
||||
msg = "image file could not be opened because AVIF support not installed"
|
||||
raise SyntaxError(msg)
|
||||
|
||||
if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available(
|
||||
DECODE_CODEC_CHOICE
|
||||
):
|
||||
msg = "Invalid opening codec"
|
||||
raise ValueError(msg)
|
||||
self._decoder = _avif.AvifDecoder(
|
||||
self.fp.read(),
|
||||
DECODE_CODEC_CHOICE,
|
||||
_get_default_max_threads(),
|
||||
)
|
||||
|
||||
# Get info from decoder
|
||||
self._size, self.n_frames, self._mode, icc, exif, exif_orientation, xmp = (
|
||||
self._decoder.get_info()
|
||||
)
|
||||
self.is_animated = self.n_frames > 1
|
||||
|
||||
if icc:
|
||||
self.info["icc_profile"] = icc
|
||||
if xmp:
|
||||
self.info["xmp"] = xmp
|
||||
|
||||
if exif_orientation != 1 or exif:
|
||||
exif_data = Image.Exif()
|
||||
if exif:
|
||||
exif_data.load(exif)
|
||||
original_orientation = exif_data.get(ExifTags.Base.Orientation, 1)
|
||||
else:
|
||||
original_orientation = 1
|
||||
if exif_orientation != original_orientation:
|
||||
exif_data[ExifTags.Base.Orientation] = exif_orientation
|
||||
exif = exif_data.tobytes()
|
||||
if exif:
|
||||
self.info["exif"] = exif
|
||||
self.seek(0)
|
||||
|
||||
def seek(self, frame: int) -> None:
|
||||
if not self._seek_check(frame):
|
||||
return
|
||||
|
||||
# Set tile
|
||||
self.__frame = frame
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)]
|
||||
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self.tile:
|
||||
# We need to load the image data for this frame
|
||||
data, timescale, pts_in_timescales, duration_in_timescales = (
|
||||
self._decoder.get_frame(self.__frame)
|
||||
)
|
||||
self.info["timestamp"] = round(1000 * (pts_in_timescales / timescale))
|
||||
self.info["duration"] = round(1000 * (duration_in_timescales / timescale))
|
||||
|
||||
if self.fp and self._exclusive_fp:
|
||||
self.fp.close()
|
||||
self.fp = BytesIO(data)
|
||||
|
||||
return super().load()
|
||||
|
||||
def load_seek(self, pos: int) -> None:
|
||||
pass
|
||||
|
||||
def tell(self) -> int:
|
||||
return self.__frame
|
||||
|
||||
|
||||
def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
_save(im, fp, filename, save_all=True)
|
||||
|
||||
|
||||
def _save(
|
||||
im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False
|
||||
) -> None:
|
||||
info = im.encoderinfo.copy()
|
||||
if save_all:
|
||||
append_images = list(info.get("append_images", []))
|
||||
else:
|
||||
append_images = []
|
||||
|
||||
total = 0
|
||||
for ims in [im] + append_images:
|
||||
total += getattr(ims, "n_frames", 1)
|
||||
|
||||
quality = info.get("quality", 75)
|
||||
if not isinstance(quality, int) or quality < 0 or quality > 100:
|
||||
msg = "Invalid quality setting"
|
||||
raise ValueError(msg)
|
||||
|
||||
duration = info.get("duration", 0)
|
||||
subsampling = info.get("subsampling", "4:2:0")
|
||||
speed = info.get("speed", 6)
|
||||
max_threads = info.get("max_threads", _get_default_max_threads())
|
||||
codec = info.get("codec", "auto")
|
||||
if codec != "auto" and not _avif.encoder_codec_available(codec):
|
||||
msg = "Invalid saving codec"
|
||||
raise ValueError(msg)
|
||||
range_ = info.get("range", "full")
|
||||
tile_rows_log2 = info.get("tile_rows", 0)
|
||||
tile_cols_log2 = info.get("tile_cols", 0)
|
||||
alpha_premultiplied = bool(info.get("alpha_premultiplied", False))
|
||||
autotiling = bool(info.get("autotiling", tile_rows_log2 == tile_cols_log2 == 0))
|
||||
|
||||
icc_profile = info.get("icc_profile", im.info.get("icc_profile"))
|
||||
exif_orientation = 1
|
||||
if exif := info.get("exif"):
|
||||
if isinstance(exif, Image.Exif):
|
||||
exif_data = exif
|
||||
else:
|
||||
exif_data = Image.Exif()
|
||||
exif_data.load(exif)
|
||||
if ExifTags.Base.Orientation in exif_data:
|
||||
exif_orientation = exif_data.pop(ExifTags.Base.Orientation)
|
||||
exif = exif_data.tobytes() if exif_data else b""
|
||||
elif isinstance(exif, Image.Exif):
|
||||
exif = exif_data.tobytes()
|
||||
|
||||
xmp = info.get("xmp")
|
||||
|
||||
if isinstance(xmp, str):
|
||||
xmp = xmp.encode("utf-8")
|
||||
|
||||
advanced = info.get("advanced")
|
||||
if advanced is not None:
|
||||
if isinstance(advanced, dict):
|
||||
advanced = advanced.items()
|
||||
try:
|
||||
advanced = tuple(advanced)
|
||||
except TypeError:
|
||||
invalid = True
|
||||
else:
|
||||
invalid = any(not isinstance(v, tuple) or len(v) != 2 for v in advanced)
|
||||
if invalid:
|
||||
msg = (
|
||||
"advanced codec options must be a dict of key-value string "
|
||||
"pairs or a series of key-value two-tuples"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Setup the AVIF encoder
|
||||
enc = _avif.AvifEncoder(
|
||||
im.size,
|
||||
subsampling,
|
||||
quality,
|
||||
speed,
|
||||
max_threads,
|
||||
codec,
|
||||
range_,
|
||||
tile_rows_log2,
|
||||
tile_cols_log2,
|
||||
alpha_premultiplied,
|
||||
autotiling,
|
||||
icc_profile or b"",
|
||||
exif or b"",
|
||||
exif_orientation,
|
||||
xmp or b"",
|
||||
advanced,
|
||||
)
|
||||
|
||||
# Add each frame
|
||||
frame_idx = 0
|
||||
frame_duration = 0
|
||||
cur_idx = im.tell()
|
||||
is_single_frame = total == 1
|
||||
try:
|
||||
for ims in [im] + append_images:
|
||||
# Get number of frames in this image
|
||||
nfr = getattr(ims, "n_frames", 1)
|
||||
|
||||
for idx in range(nfr):
|
||||
ims.seek(idx)
|
||||
|
||||
# Make sure image mode is supported
|
||||
frame = ims
|
||||
rawmode = ims.mode
|
||||
if ims.mode not in {"RGB", "RGBA"}:
|
||||
rawmode = "RGBA" if ims.has_transparency_data else "RGB"
|
||||
frame = ims.convert(rawmode)
|
||||
|
||||
# Update frame duration
|
||||
if isinstance(duration, (list, tuple)):
|
||||
frame_duration = duration[frame_idx]
|
||||
else:
|
||||
frame_duration = duration
|
||||
|
||||
# Append the frame to the animation encoder
|
||||
enc.add(
|
||||
frame.tobytes("raw", rawmode),
|
||||
frame_duration,
|
||||
frame.size,
|
||||
rawmode,
|
||||
is_single_frame,
|
||||
)
|
||||
|
||||
# Update frame index
|
||||
frame_idx += 1
|
||||
|
||||
if not save_all:
|
||||
break
|
||||
|
||||
finally:
|
||||
im.seek(cur_idx)
|
||||
|
||||
# Get the final output from the encoder
|
||||
data = enc.finish()
|
||||
if data is None:
|
||||
msg = "cannot write file as AVIF (encoder returned None)"
|
||||
raise OSError(msg)
|
||||
|
||||
fp.write(data)
|
||||
|
||||
|
||||
Image.register_open(AvifImageFile.format, AvifImageFile, _accept)
|
||||
if SUPPORTED:
|
||||
Image.register_save(AvifImageFile.format, _save)
|
||||
Image.register_save_all(AvifImageFile.format, _save_all)
|
||||
Image.register_extensions(AvifImageFile.format, [".avif", ".avifs"])
|
||||
Image.register_mime(AvifImageFile.format, "image/avif")
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
#
|
||||
# The Python Imaging Library
|
||||
# $Id$
|
||||
#
|
||||
# bitmap distribution font (bdf) file parser
|
||||
#
|
||||
# history:
|
||||
# 1996-05-16 fl created (as bdf2pil)
|
||||
# 1997-08-25 fl converted to FontFile driver
|
||||
# 2001-05-25 fl removed bogus __init__ call
|
||||
# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev)
|
||||
# 2003-04-22 fl more robustification (from Graham Dumpleton)
|
||||
#
|
||||
# Copyright (c) 1997-2003 by Secret Labs AB.
|
||||
# Copyright (c) 1997-2003 by Fredrik Lundh.
|
||||
#
|
||||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
"""
|
||||
Parse X Bitmap Distribution Format (BDF)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import BinaryIO
|
||||
|
||||
from . import FontFile, Image
|
||||
|
||||
|
||||
def bdf_char(
|
||||
f: BinaryIO,
|
||||
) -> (
|
||||
tuple[
|
||||
str,
|
||||
int,
|
||||
tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]],
|
||||
Image.Image,
|
||||
]
|
||||
| None
|
||||
):
|
||||
# skip to STARTCHAR
|
||||
while True:
|
||||
s = f.readline()
|
||||
if not s:
|
||||
return None
|
||||
if s.startswith(b"STARTCHAR"):
|
||||
break
|
||||
id = s[9:].strip().decode("ascii")
|
||||
|
||||
# load symbol properties
|
||||
props = {}
|
||||
while True:
|
||||
s = f.readline()
|
||||
if not s or s.startswith(b"BITMAP"):
|
||||
break
|
||||
i = s.find(b" ")
|
||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
||||
|
||||
# load bitmap
|
||||
bitmap = bytearray()
|
||||
while True:
|
||||
s = f.readline()
|
||||
if not s or s.startswith(b"ENDCHAR"):
|
||||
break
|
||||
bitmap += s[:-1]
|
||||
|
||||
# The word BBX
|
||||
# followed by the width in x (BBw), height in y (BBh),
|
||||
# and x and y displacement (BBxoff0, BByoff0)
|
||||
# of the lower left corner from the origin of the character.
|
||||
width, height, x_disp, y_disp = (int(p) for p in props["BBX"].split())
|
||||
|
||||
# The word DWIDTH
|
||||
# followed by the width in x and y of the character in device pixels.
|
||||
dwx, dwy = (int(p) for p in props["DWIDTH"].split())
|
||||
|
||||
bbox = (
|
||||
(dwx, dwy),
|
||||
(x_disp, -y_disp - height, width + x_disp, -y_disp),
|
||||
(0, 0, width, height),
|
||||
)
|
||||
|
||||
try:
|
||||
im = Image.frombytes("1", (width, height), bitmap, "hex", "1")
|
||||
except ValueError:
|
||||
# deal with zero-width characters
|
||||
im = Image.new("1", (width, height))
|
||||
|
||||
return id, int(props["ENCODING"]), bbox, im
|
||||
|
||||
|
||||
class BdfFontFile(FontFile.FontFile):
|
||||
"""Font file plugin for the X11 BDF format."""
|
||||
|
||||
def __init__(self, fp: BinaryIO) -> None:
|
||||
super().__init__()
|
||||
|
||||
s = fp.readline()
|
||||
if not s.startswith(b"STARTFONT 2.1"):
|
||||
msg = "not a valid BDF file"
|
||||
raise SyntaxError(msg)
|
||||
|
||||
props = {}
|
||||
comments = []
|
||||
|
||||
while True:
|
||||
s = fp.readline()
|
||||
if not s or s.startswith(b"ENDPROPERTIES"):
|
||||
break
|
||||
i = s.find(b" ")
|
||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
||||
if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
|
||||
if s.find(b"LogicalFontDescription") < 0:
|
||||
comments.append(s[i + 1 : -1].decode("ascii"))
|
||||
|
||||
while True:
|
||||
c = bdf_char(fp)
|
||||
if not c:
|
||||
break
|
||||
id, ch, (xy, dst, src), im = c
|
||||
if 0 <= ch < len(self.glyph):
|
||||
self.glyph[ch] = xy, dst, src, im
|
||||
|
|
@ -1,497 +0,0 @@
|
|||
"""
|
||||
Blizzard Mipmap Format (.blp)
|
||||
Jerome Leclanche <jerome@leclan.ch>
|
||||
|
||||
The contents of this file are hereby released in the public domain (CC0)
|
||||
Full text of the CC0 license:
|
||||
https://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
BLP1 files, used mostly in Warcraft III, are not fully supported.
|
||||
All types of BLP2 files used in World of Warcraft are supported.
|
||||
|
||||
The BLP file structure consists of a header, up to 16 mipmaps of the
|
||||
texture
|
||||
|
||||
Texture sizes must be powers of two, though the two dimensions do
|
||||
not have to be equal; 512x256 is valid, but 512x200 is not.
|
||||
The first mipmap (mipmap #0) is the full size image; each subsequent
|
||||
mipmap halves both dimensions. The final mipmap should be 1x1.
|
||||
|
||||
BLP files come in many different flavours:
|
||||
* JPEG-compressed (type == 0) - only supported for BLP1.
|
||||
* RAW images (type == 1, encoding == 1). Each mipmap is stored as an
|
||||
array of 8-bit values, one per pixel, left to right, top to bottom.
|
||||
Each value is an index to the palette.
|
||||
* DXT-compressed (type == 1, encoding == 2):
|
||||
- DXT1 compression is used if alpha_encoding == 0.
|
||||
- An additional alpha bit is used if alpha_depth == 1.
|
||||
- DXT3 compression is used if alpha_encoding == 1.
|
||||
- DXT5 compression is used if alpha_encoding == 7.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import os
|
||||
import struct
|
||||
from enum import IntEnum
|
||||
from io import BytesIO
|
||||
from typing import IO
|
||||
|
||||
from . import Image, ImageFile
|
||||
|
||||
|
||||
class Format(IntEnum):
|
||||
JPEG = 0
|
||||
|
||||
|
||||
class Encoding(IntEnum):
|
||||
UNCOMPRESSED = 1
|
||||
DXT = 2
|
||||
UNCOMPRESSED_RAW_BGRA = 3
|
||||
|
||||
|
||||
class AlphaEncoding(IntEnum):
|
||||
DXT1 = 0
|
||||
DXT3 = 1
|
||||
DXT5 = 7
|
||||
|
||||
|
||||
def unpack_565(i: int) -> tuple[int, int, int]:
|
||||
return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3
|
||||
|
||||
|
||||
def decode_dxt1(
|
||||
data: bytes, alpha: bool = False
|
||||
) -> tuple[bytearray, bytearray, bytearray, bytearray]:
|
||||
"""
|
||||
input: one "row" of data (i.e. will produce 4*width pixels)
|
||||
"""
|
||||
|
||||
blocks = len(data) // 8 # number of blocks in row
|
||||
ret = (bytearray(), bytearray(), bytearray(), bytearray())
|
||||
|
||||
for block_index in range(blocks):
|
||||
# Decode next 8-byte block.
|
||||
idx = block_index * 8
|
||||
color0, color1, bits = struct.unpack_from("<HHI", data, idx)
|
||||
|
||||
r0, g0, b0 = unpack_565(color0)
|
||||
r1, g1, b1 = unpack_565(color1)
|
||||
|
||||
# Decode this block into 4x4 pixels
|
||||
# Accumulate the results onto our 4 row accumulators
|
||||
for j in range(4):
|
||||
for i in range(4):
|
||||
# get next control op and generate a pixel
|
||||
|
||||
control = bits & 3
|
||||
bits = bits >> 2
|
||||
|
||||
a = 0xFF
|
||||
if control == 0:
|
||||
r, g, b = r0, g0, b0
|
||||
elif control == 1:
|
||||
r, g, b = r1, g1, b1
|
||||
elif control == 2:
|
||||
if color0 > color1:
|
||||
r = (2 * r0 + r1) // 3
|
||||
g = (2 * g0 + g1) // 3
|
||||
b = (2 * b0 + b1) // 3
|
||||
else:
|
||||
r = (r0 + r1) // 2
|
||||
g = (g0 + g1) // 2
|
||||
b = (b0 + b1) // 2
|
||||
elif control == 3:
|
||||
if color0 > color1:
|
||||
r = (2 * r1 + r0) // 3
|
||||
g = (2 * g1 + g0) // 3
|
||||
b = (2 * b1 + b0) // 3
|
||||
else:
|
||||
r, g, b, a = 0, 0, 0, 0
|
||||
|
||||
if alpha:
|
||||
ret[j].extend([r, g, b, a])
|
||||
else:
|
||||
ret[j].extend([r, g, b])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]:
|
||||
"""
|
||||
input: one "row" of data (i.e. will produce 4*width pixels)
|
||||
"""
|
||||
|
||||
blocks = len(data) // 16 # number of blocks in row
|
||||
ret = (bytearray(), bytearray(), bytearray(), bytearray())
|
||||
|
||||
for block_index in range(blocks):
|
||||
idx = block_index * 16
|
||||
block = data[idx : idx + 16]
|
||||
# Decode next 16-byte block.
|
||||
bits = struct.unpack_from("<8B", block)
|
||||
color0, color1 = struct.unpack_from("<HH", block, 8)
|
||||
|
||||
(code,) = struct.unpack_from("<I", block, 12)
|
||||
|
||||
r0, g0, b0 = unpack_565(color0)
|
||||
r1, g1, b1 = unpack_565(color1)
|
||||
|
||||
for j in range(4):
|
||||
high = False # Do we want the higher bits?
|
||||
for i in range(4):
|
||||
alphacode_index = (4 * j + i) // 2
|
||||
a = bits[alphacode_index]
|
||||
if high:
|
||||
high = False
|
||||
a >>= 4
|
||||
else:
|
||||
high = True
|
||||
a &= 0xF
|
||||
a *= 17 # We get a value between 0 and 15
|
||||
|
||||
color_code = (code >> 2 * (4 * j + i)) & 0x03
|
||||
|
||||
if color_code == 0:
|
||||
r, g, b = r0, g0, b0
|
||||
elif color_code == 1:
|
||||
r, g, b = r1, g1, b1
|
||||
elif color_code == 2:
|
||||
r = (2 * r0 + r1) // 3
|
||||
g = (2 * g0 + g1) // 3
|
||||
b = (2 * b0 + b1) // 3
|
||||
elif color_code == 3:
|
||||
r = (2 * r1 + r0) // 3
|
||||
g = (2 * g1 + g0) // 3
|
||||
b = (2 * b1 + b0) // 3
|
||||
|
||||
ret[j].extend([r, g, b, a])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]:
|
||||
"""
|
||||
input: one "row" of data (i.e. will produce 4 * width pixels)
|
||||
"""
|
||||
|
||||
blocks = len(data) // 16 # number of blocks in row
|
||||
ret = (bytearray(), bytearray(), bytearray(), bytearray())
|
||||
|
||||
for block_index in range(blocks):
|
||||
idx = block_index * 16
|
||||
block = data[idx : idx + 16]
|
||||
# Decode next 16-byte block.
|
||||
a0, a1 = struct.unpack_from("<BB", block)
|
||||
|
||||
bits = struct.unpack_from("<6B", block, 2)
|
||||
alphacode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
|
||||
alphacode2 = bits[0] | (bits[1] << 8)
|
||||
|
||||
color0, color1 = struct.unpack_from("<HH", block, 8)
|
||||
|
||||
(code,) = struct.unpack_from("<I", block, 12)
|
||||
|
||||
r0, g0, b0 = unpack_565(color0)
|
||||
r1, g1, b1 = unpack_565(color1)
|
||||
|
||||
for j in range(4):
|
||||
for i in range(4):
|
||||
# get next control op and generate a pixel
|
||||
alphacode_index = 3 * (4 * j + i)
|
||||
|
||||
if alphacode_index <= 12:
|
||||
alphacode = (alphacode2 >> alphacode_index) & 0x07
|
||||
elif alphacode_index == 15:
|
||||
alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06)
|
||||
else: # alphacode_index >= 18 and alphacode_index <= 45
|
||||
alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07
|
||||
|
||||
if alphacode == 0:
|
||||
a = a0
|
||||
elif alphacode == 1:
|
||||
a = a1
|
||||
elif a0 > a1:
|
||||
a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7
|
||||
elif alphacode == 6:
|
||||
a = 0
|
||||
elif alphacode == 7:
|
||||
a = 255
|
||||
else:
|
||||
a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5
|
||||
|
||||
color_code = (code >> 2 * (4 * j + i)) & 0x03
|
||||
|
||||
if color_code == 0:
|
||||
r, g, b = r0, g0, b0
|
||||
elif color_code == 1:
|
||||
r, g, b = r1, g1, b1
|
||||
elif color_code == 2:
|
||||
r = (2 * r0 + r1) // 3
|
||||
g = (2 * g0 + g1) // 3
|
||||
b = (2 * b0 + b1) // 3
|
||||
elif color_code == 3:
|
||||
r = (2 * r1 + r0) // 3
|
||||
g = (2 * g1 + g0) // 3
|
||||
b = (2 * b1 + b0) // 3
|
||||
|
||||
ret[j].extend([r, g, b, a])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class BLPFormatError(NotImplementedError):
|
||||
pass
|
||||
|
||||
|
||||
def _accept(prefix: bytes) -> bool:
|
||||
return prefix.startswith((b"BLP1", b"BLP2"))
|
||||
|
||||
|
||||
class BlpImageFile(ImageFile.ImageFile):
|
||||
"""
|
||||
Blizzard Mipmap Format
|
||||
"""
|
||||
|
||||
format = "BLP"
|
||||
format_description = "Blizzard Mipmap Format"
|
||||
|
||||
def _open(self) -> None:
|
||||
self.magic = self.fp.read(4)
|
||||
if not _accept(self.magic):
|
||||
msg = f"Bad BLP magic {repr(self.magic)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
compression = struct.unpack("<i", self.fp.read(4))[0]
|
||||
if self.magic == b"BLP1":
|
||||
alpha = struct.unpack("<I", self.fp.read(4))[0] != 0
|
||||
else:
|
||||
encoding = struct.unpack("<b", self.fp.read(1))[0]
|
||||
alpha = struct.unpack("<b", self.fp.read(1))[0] != 0
|
||||
alpha_encoding = struct.unpack("<b", self.fp.read(1))[0]
|
||||
self.fp.seek(1, os.SEEK_CUR) # mips
|
||||
|
||||
self._size = struct.unpack("<II", self.fp.read(8))
|
||||
|
||||
args: tuple[int, int, bool] | tuple[int, int, bool, int]
|
||||
if self.magic == b"BLP1":
|
||||
encoding = struct.unpack("<i", self.fp.read(4))[0]
|
||||
self.fp.seek(4, os.SEEK_CUR) # subtype
|
||||
|
||||
args = (compression, encoding, alpha)
|
||||
offset = 28
|
||||
else:
|
||||
args = (compression, encoding, alpha, alpha_encoding)
|
||||
offset = 20
|
||||
|
||||
decoder = self.magic.decode()
|
||||
|
||||
self._mode = "RGBA" if alpha else "RGB"
|
||||
self.tile = [ImageFile._Tile(decoder, (0, 0) + self.size, offset, args)]
|
||||
|
||||
|
||||
class _BLPBaseDecoder(abc.ABC, ImageFile.PyDecoder):
|
||||
_pulls_fd = True
|
||||
|
||||
def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
|
||||
try:
|
||||
self._read_header()
|
||||
self._load()
|
||||
except struct.error as e:
|
||||
msg = "Truncated BLP file"
|
||||
raise OSError(msg) from e
|
||||
return -1, 0
|
||||
|
||||
@abc.abstractmethod
|
||||
def _load(self) -> None:
|
||||
pass
|
||||
|
||||
def _read_header(self) -> None:
|
||||
self._offsets = struct.unpack("<16I", self._safe_read(16 * 4))
|
||||
self._lengths = struct.unpack("<16I", self._safe_read(16 * 4))
|
||||
|
||||
def _safe_read(self, length: int) -> bytes:
|
||||
assert self.fd is not None
|
||||
return ImageFile._safe_read(self.fd, length)
|
||||
|
||||
def _read_palette(self) -> list[tuple[int, int, int, int]]:
|
||||
ret = []
|
||||
for i in range(256):
|
||||
try:
|
||||
b, g, r, a = struct.unpack("<4B", self._safe_read(4))
|
||||
except struct.error:
|
||||
break
|
||||
ret.append((b, g, r, a))
|
||||
return ret
|
||||
|
||||
def _read_bgra(
|
||||
self, palette: list[tuple[int, int, int, int]], alpha: bool
|
||||
) -> bytearray:
|
||||
data = bytearray()
|
||||
_data = BytesIO(self._safe_read(self._lengths[0]))
|
||||
while True:
|
||||
try:
|
||||
(offset,) = struct.unpack("<B", _data.read(1))
|
||||
except struct.error:
|
||||
break
|
||||
b, g, r, a = palette[offset]
|
||||
d: tuple[int, ...] = (r, g, b)
|
||||
if alpha:
|
||||
d += (a,)
|
||||
data.extend(d)
|
||||
return data
|
||||
|
||||
|
||||
class BLP1Decoder(_BLPBaseDecoder):
|
||||
def _load(self) -> None:
|
||||
self._compression, self._encoding, alpha = self.args
|
||||
|
||||
if self._compression == Format.JPEG:
|
||||
self._decode_jpeg_stream()
|
||||
|
||||
elif self._compression == 1:
|
||||
if self._encoding in (4, 5):
|
||||
palette = self._read_palette()
|
||||
data = self._read_bgra(palette, alpha)
|
||||
self.set_as_raw(data)
|
||||
else:
|
||||
msg = f"Unsupported BLP encoding {repr(self._encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
else:
|
||||
msg = f"Unsupported BLP compression {repr(self._encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
def _decode_jpeg_stream(self) -> None:
|
||||
from .JpegImagePlugin import JpegImageFile
|
||||
|
||||
(jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
|
||||
jpeg_header = self._safe_read(jpeg_header_size)
|
||||
assert self.fd is not None
|
||||
self._safe_read(self._offsets[0] - self.fd.tell()) # What IS this?
|
||||
data = self._safe_read(self._lengths[0])
|
||||
data = jpeg_header + data
|
||||
image = JpegImageFile(BytesIO(data))
|
||||
Image._decompression_bomb_check(image.size)
|
||||
if image.mode == "CMYK":
|
||||
args = image.tile[0].args
|
||||
assert isinstance(args, tuple)
|
||||
image.tile = [image.tile[0]._replace(args=(args[0], "CMYK"))]
|
||||
self.set_as_raw(image.convert("RGB").tobytes(), "BGR")
|
||||
|
||||
|
||||
class BLP2Decoder(_BLPBaseDecoder):
|
||||
def _load(self) -> None:
|
||||
self._compression, self._encoding, alpha, self._alpha_encoding = self.args
|
||||
|
||||
palette = self._read_palette()
|
||||
|
||||
assert self.fd is not None
|
||||
self.fd.seek(self._offsets[0])
|
||||
|
||||
if self._compression == 1:
|
||||
# Uncompressed or DirectX compression
|
||||
|
||||
if self._encoding == Encoding.UNCOMPRESSED:
|
||||
data = self._read_bgra(palette, alpha)
|
||||
|
||||
elif self._encoding == Encoding.DXT:
|
||||
data = bytearray()
|
||||
if self._alpha_encoding == AlphaEncoding.DXT1:
|
||||
linesize = (self.state.xsize + 3) // 4 * 8
|
||||
for yb in range((self.state.ysize + 3) // 4):
|
||||
for d in decode_dxt1(self._safe_read(linesize), alpha):
|
||||
data += d
|
||||
|
||||
elif self._alpha_encoding == AlphaEncoding.DXT3:
|
||||
linesize = (self.state.xsize + 3) // 4 * 16
|
||||
for yb in range((self.state.ysize + 3) // 4):
|
||||
for d in decode_dxt3(self._safe_read(linesize)):
|
||||
data += d
|
||||
|
||||
elif self._alpha_encoding == AlphaEncoding.DXT5:
|
||||
linesize = (self.state.xsize + 3) // 4 * 16
|
||||
for yb in range((self.state.ysize + 3) // 4):
|
||||
for d in decode_dxt5(self._safe_read(linesize)):
|
||||
data += d
|
||||
else:
|
||||
msg = f"Unsupported alpha encoding {repr(self._alpha_encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
else:
|
||||
msg = f"Unknown BLP encoding {repr(self._encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
else:
|
||||
msg = f"Unknown BLP compression {repr(self._compression)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
self.set_as_raw(data)
|
||||
|
||||
|
||||
class BLPEncoder(ImageFile.PyEncoder):
|
||||
_pushes_fd = True
|
||||
|
||||
def _write_palette(self) -> bytes:
|
||||
data = b""
|
||||
assert self.im is not None
|
||||
palette = self.im.getpalette("RGBA", "RGBA")
|
||||
for i in range(len(palette) // 4):
|
||||
r, g, b, a = palette[i * 4 : (i + 1) * 4]
|
||||
data += struct.pack("<4B", b, g, r, a)
|
||||
while len(data) < 256 * 4:
|
||||
data += b"\x00" * 4
|
||||
return data
|
||||
|
||||
def encode(self, bufsize: int) -> tuple[int, int, bytes]:
|
||||
palette_data = self._write_palette()
|
||||
|
||||
offset = 20 + 16 * 4 * 2 + len(palette_data)
|
||||
data = struct.pack("<16I", offset, *((0,) * 15))
|
||||
|
||||
assert self.im is not None
|
||||
w, h = self.im.size
|
||||
data += struct.pack("<16I", w * h, *((0,) * 15))
|
||||
|
||||
data += palette_data
|
||||
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
data += struct.pack("<B", self.im.getpixel((x, y)))
|
||||
|
||||
return len(data), 0, data
|
||||
|
||||
|
||||
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
if im.mode != "P":
|
||||
msg = "Unsupported BLP image mode"
|
||||
raise ValueError(msg)
|
||||
|
||||
magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2"
|
||||
fp.write(magic)
|
||||
|
||||
assert im.palette is not None
|
||||
fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression
|
||||
|
||||
alpha_depth = 1 if im.palette.mode == "RGBA" else 0
|
||||
if magic == b"BLP1":
|
||||
fp.write(struct.pack("<L", alpha_depth))
|
||||
else:
|
||||
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
|
||||
fp.write(struct.pack("<b", alpha_depth))
|
||||
fp.write(struct.pack("<b", 0)) # alpha encoding
|
||||
fp.write(struct.pack("<b", 0)) # mips
|
||||
fp.write(struct.pack("<II", *im.size))
|
||||
if magic == b"BLP1":
|
||||
fp.write(struct.pack("<i", 5))
|
||||
fp.write(struct.pack("<i", 0))
|
||||
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("BLP", (0, 0) + im.size, 0, im.mode)])
|
||||
|
||||
|
||||
Image.register_open(BlpImageFile.format, BlpImageFile, _accept)
|
||||
Image.register_extension(BlpImageFile.format, ".blp")
|
||||
Image.register_decoder("BLP1", BLP1Decoder)
|
||||
Image.register_decoder("BLP2", BLP2Decoder)
|
||||
|
||||
Image.register_save(BlpImageFile.format, _save)
|
||||
Image.register_encoder("BLP", BLPEncoder)
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
#
|
||||
# The Python Imaging Library.
|
||||
# $Id$
|
||||
#
|
||||
# BMP file handler
|
||||
#
|
||||
# Windows (and OS/2) native bitmap storage format.
|
||||
#
|
||||
# history:
|
||||
# 1995-09-01 fl Created
|
||||
# 1996-04-30 fl Added save
|
||||
# 1997-08-27 fl Fixed save of 1-bit images
|
||||
# 1998-03-06 fl Load P images as L where possible
|
||||
# 1998-07-03 fl Load P images as 1 where possible
|
||||
# 1998-12-29 fl Handle small palettes
|
||||
# 2002-12-30 fl Fixed load of 1-bit palette images
|
||||
# 2003-04-21 fl Fixed load of 1-bit monochrome images
|
||||
# 2003-04-23 fl Added limited support for BI_BITFIELDS compression
|
||||
#
|
||||
# Copyright (c) 1997-2003 by Secret Labs AB
|
||||
# Copyright (c) 1995-2003 by Fredrik Lundh
|
||||
#
|
||||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import IO, Any
|
||||
|
||||
from . import Image, ImageFile, ImagePalette
|
||||
from ._binary import i16le as i16
|
||||
from ._binary import i32le as i32
|
||||
from ._binary import o8
|
||||
from ._binary import o16le as o16
|
||||
from ._binary import o32le as o32
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
# Read BMP file
|
||||
|
||||
BIT2MODE = {
|
||||
# bits => mode, rawmode
|
||||
1: ("P", "P;1"),
|
||||
4: ("P", "P;4"),
|
||||
8: ("P", "P"),
|
||||
16: ("RGB", "BGR;15"),
|
||||
24: ("RGB", "BGR"),
|
||||
32: ("RGB", "BGRX"),
|
||||
}
|
||||
|
||||
USE_RAW_ALPHA = False
|
||||
|
||||
|
||||
def _accept(prefix: bytes) -> bool:
|
||||
return prefix.startswith(b"BM")
|
||||
|
||||
|
||||
def _dib_accept(prefix: bytes) -> bool:
|
||||
return i32(prefix) in [12, 40, 52, 56, 64, 108, 124]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Image plugin for the Windows BMP format.
|
||||
# =============================================================================
|
||||
class BmpImageFile(ImageFile.ImageFile):
|
||||
"""Image plugin for the Windows Bitmap format (BMP)"""
|
||||
|
||||
# ------------------------------------------------------------- Description
|
||||
format_description = "Windows Bitmap"
|
||||
format = "BMP"
|
||||
|
||||
# -------------------------------------------------- BMP Compression values
|
||||
COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
|
||||
for k, v in COMPRESSIONS.items():
|
||||
vars()[k] = v
|
||||
|
||||
def _bitmap(self, header: int = 0, offset: int = 0) -> None:
|
||||
"""Read relevant info about the BMP"""
|
||||
read, seek = self.fp.read, self.fp.seek
|
||||
if header:
|
||||
seek(header)
|
||||
# read bmp header size @offset 14 (this is part of the header size)
|
||||
file_info: dict[str, bool | int | tuple[int, ...]] = {
|
||||
"header_size": i32(read(4)),
|
||||
"direction": -1,
|
||||
}
|
||||
|
||||
# -------------------- If requested, read header at a specific position
|
||||
# read the rest of the bmp header, without its size
|
||||
assert isinstance(file_info["header_size"], int)
|
||||
header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)
|
||||
|
||||
# ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1
|
||||
# ----- This format has different offsets because of width/height types
|
||||
# 12: BITMAPCOREHEADER/OS21XBITMAPHEADER
|
||||
if file_info["header_size"] == 12:
|
||||
file_info["width"] = i16(header_data, 0)
|
||||
file_info["height"] = i16(header_data, 2)
|
||||
file_info["planes"] = i16(header_data, 4)
|
||||
file_info["bits"] = i16(header_data, 6)
|
||||
file_info["compression"] = self.COMPRESSIONS["RAW"]
|
||||
file_info["palette_padding"] = 3
|
||||
|
||||
# --------------------------------------------- Windows Bitmap v3 to v5
|
||||
# 40: BITMAPINFOHEADER
|
||||
# 52: BITMAPV2HEADER
|
||||
# 56: BITMAPV3HEADER
|
||||
# 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER
|
||||
# 108: BITMAPV4HEADER
|
||||
# 124: BITMAPV5HEADER
|
||||
elif file_info["header_size"] in (40, 52, 56, 64, 108, 124):
|
||||
file_info["y_flip"] = header_data[7] == 0xFF
|
||||
file_info["direction"] = 1 if file_info["y_flip"] else -1
|
||||
file_info["width"] = i32(header_data, 0)
|
||||
file_info["height"] = (
|
||||
i32(header_data, 4)
|
||||
if not file_info["y_flip"]
|
||||
else 2**32 - i32(header_data, 4)
|
||||
)
|
||||
file_info["planes"] = i16(header_data, 8)
|
||||
file_info["bits"] = i16(header_data, 10)
|
||||
file_info["compression"] = i32(header_data, 12)
|
||||
# byte size of pixel data
|
||||
file_info["data_size"] = i32(header_data, 16)
|
||||
file_info["pixels_per_meter"] = (
|
||||
i32(header_data, 20),
|
||||
i32(header_data, 24),
|
||||
)
|
||||
file_info["colors"] = i32(header_data, 28)
|
||||
file_info["palette_padding"] = 4
|
||||
assert isinstance(file_info["pixels_per_meter"], tuple)
|
||||
self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"])
|
||||
if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]:
|
||||
masks = ["r_mask", "g_mask", "b_mask"]
|
||||
if len(header_data) >= 48:
|
||||
if len(header_data) >= 52:
|
||||
masks.append("a_mask")
|
||||
else:
|
||||
file_info["a_mask"] = 0x0
|
||||
for idx, mask in enumerate(masks):
|
||||
file_info[mask] = i32(header_data, 36 + idx * 4)
|
||||
else:
|
||||
# 40 byte headers only have the three components in the
|
||||
# bitfields masks, ref:
|
||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
||||
# See also
|
||||
# https://github.com/python-pillow/Pillow/issues/1293
|
||||
# There is a 4th component in the RGBQuad, in the alpha
|
||||
# location, but it is listed as a reserved component,
|
||||
# and it is not generally an alpha channel
|
||||
file_info["a_mask"] = 0x0
|
||||
for mask in masks:
|
||||
file_info[mask] = i32(read(4))
|
||||
assert isinstance(file_info["r_mask"], int)
|
||||
assert isinstance(file_info["g_mask"], int)
|
||||
assert isinstance(file_info["b_mask"], int)
|
||||
assert isinstance(file_info["a_mask"], int)
|
||||
file_info["rgb_mask"] = (
|
||||
file_info["r_mask"],
|
||||
file_info["g_mask"],
|
||||
file_info["b_mask"],
|
||||
)
|
||||
file_info["rgba_mask"] = (
|
||||
file_info["r_mask"],
|
||||
file_info["g_mask"],
|
||||
file_info["b_mask"],
|
||||
file_info["a_mask"],
|
||||
)
|
||||
else:
|
||||
msg = f"Unsupported BMP header type ({file_info['header_size']})"
|
||||
raise OSError(msg)
|
||||
|
||||
# ------------------ Special case : header is reported 40, which
|
||||
# ---------------------- is shorter than real size for bpp >= 16
|
||||
assert isinstance(file_info["width"], int)
|
||||
assert isinstance(file_info["height"], int)
|
||||
self._size = file_info["width"], file_info["height"]
|
||||
|
||||
# ------- If color count was not found in the header, compute from bits
|
||||
assert isinstance(file_info["bits"], int)
|
||||
file_info["colors"] = (
|
||||
file_info["colors"]
|
||||
if file_info.get("colors", 0)
|
||||
else (1 << file_info["bits"])
|
||||
)
|
||||
assert isinstance(file_info["colors"], int)
|
||||
if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8:
|
||||
offset += 4 * file_info["colors"]
|
||||
|
||||
# ---------------------- Check bit depth for unusual unsupported values
|
||||
self._mode, raw_mode = BIT2MODE.get(file_info["bits"], ("", ""))
|
||||
if not self.mode:
|
||||
msg = f"Unsupported BMP pixel depth ({file_info['bits']})"
|
||||
raise OSError(msg)
|
||||
|
||||
# ---------------- Process BMP with Bitfields compression (not palette)
|
||||
decoder_name = "raw"
|
||||
if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]:
|
||||
SUPPORTED: dict[int, list[tuple[int, ...]]] = {
|
||||
32: [
|
||||
(0xFF0000, 0xFF00, 0xFF, 0x0),
|
||||
(0xFF000000, 0xFF0000, 0xFF00, 0x0),
|
||||
(0xFF000000, 0xFF00, 0xFF, 0x0),
|
||||
(0xFF000000, 0xFF0000, 0xFF00, 0xFF),
|
||||
(0xFF, 0xFF00, 0xFF0000, 0xFF000000),
|
||||
(0xFF0000, 0xFF00, 0xFF, 0xFF000000),
|
||||
(0xFF000000, 0xFF00, 0xFF, 0xFF0000),
|
||||
(0x0, 0x0, 0x0, 0x0),
|
||||
],
|
||||
24: [(0xFF0000, 0xFF00, 0xFF)],
|
||||
16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
|
||||
}
|
||||
MASK_MODES = {
|
||||
(32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
|
||||
(32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
|
||||
(32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR",
|
||||
(32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR",
|
||||
(32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
|
||||
(32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
|
||||
(32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR",
|
||||
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
||||
(24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
|
||||
(16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
|
||||
(16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
|
||||
}
|
||||
if file_info["bits"] in SUPPORTED:
|
||||
if (
|
||||
file_info["bits"] == 32
|
||||
and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
|
||||
):
|
||||
assert isinstance(file_info["rgba_mask"], tuple)
|
||||
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
|
||||
self._mode = "RGBA" if "A" in raw_mode else self.mode
|
||||
elif (
|
||||
file_info["bits"] in (24, 16)
|
||||
and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
|
||||
):
|
||||
assert isinstance(file_info["rgb_mask"], tuple)
|
||||
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
|
||||
else:
|
||||
msg = "Unsupported BMP bitfields layout"
|
||||
raise OSError(msg)
|
||||
else:
|
||||
msg = "Unsupported BMP bitfields layout"
|
||||
raise OSError(msg)
|
||||
elif file_info["compression"] == self.COMPRESSIONS["RAW"]:
|
||||
if file_info["bits"] == 32 and (
|
||||
header == 22 or USE_RAW_ALPHA # 32-bit .cur offset
|
||||
):
|
||||
raw_mode, self._mode = "BGRA", "RGBA"
|
||||
elif file_info["compression"] in (
|
||||
self.COMPRESSIONS["RLE8"],
|
||||
self.COMPRESSIONS["RLE4"],
|
||||
):
|
||||
decoder_name = "bmp_rle"
|
||||
else:
|
||||
msg = f"Unsupported BMP compression ({file_info['compression']})"
|
||||
raise OSError(msg)
|
||||
|
||||
# --------------- Once the header is processed, process the palette/LUT
|
||||
if self.mode == "P": # Paletted for 1, 4 and 8 bit images
|
||||
# ---------------------------------------------------- 1-bit images
|
||||
if not (0 < file_info["colors"] <= 65536):
|
||||
msg = f"Unsupported BMP Palette size ({file_info['colors']})"
|
||||
raise OSError(msg)
|
||||
else:
|
||||
assert isinstance(file_info["palette_padding"], int)
|
||||
padding = file_info["palette_padding"]
|
||||
palette = read(padding * file_info["colors"])
|
||||
grayscale = True
|
||||
indices = (
|
||||
(0, 255)
|
||||
if file_info["colors"] == 2
|
||||
else list(range(file_info["colors"]))
|
||||
)
|
||||
|
||||
# ----------------- Check if grayscale and ignore palette if so
|
||||
for ind, val in enumerate(indices):
|
||||
rgb = palette[ind * padding : ind * padding + 3]
|
||||
if rgb != o8(val) * 3:
|
||||
grayscale = False
|
||||
|
||||
# ------- If all colors are gray, white or black, ditch palette
|
||||
if grayscale:
|
||||
self._mode = "1" if file_info["colors"] == 2 else "L"
|
||||
raw_mode = self.mode
|
||||
else:
|
||||
self._mode = "P"
|
||||
self.palette = ImagePalette.raw(
|
||||
"BGRX" if padding == 4 else "BGR", palette
|
||||
)
|
||||
|
||||
# ---------------------------- Finally set the tile data for the plugin
|
||||
self.info["compression"] = file_info["compression"]
|
||||
args: list[Any] = [raw_mode]
|
||||
if decoder_name == "bmp_rle":
|
||||
args.append(file_info["compression"] == self.COMPRESSIONS["RLE4"])
|
||||
else:
|
||||
assert isinstance(file_info["width"], int)
|
||||
args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3))
|
||||
args.append(file_info["direction"])
|
||||
self.tile = [
|
||||
ImageFile._Tile(
|
||||
decoder_name,
|
||||
(0, 0, file_info["width"], file_info["height"]),
|
||||
offset or self.fp.tell(),
|
||||
tuple(args),
|
||||
)
|
||||
]
|
||||
|
||||
def _open(self) -> None:
|
||||
"""Open file, check magic number and read header"""
|
||||
# read 14 bytes: magic number, filesize, reserved, header final offset
|
||||
head_data = self.fp.read(14)
|
||||
# choke if the file does not have the required magic bytes
|
||||
if not _accept(head_data):
|
||||
msg = "Not a BMP file"
|
||||
raise SyntaxError(msg)
|
||||
# read the start position of the BMP image data (u32)
|
||||
offset = i32(head_data, 10)
|
||||
# load bitmap information (offset=raster info)
|
||||
self._bitmap(offset=offset)
|
||||
|
||||
|
||||
class BmpRleDecoder(ImageFile.PyDecoder):
|
||||
_pulls_fd = True
|
||||
|
||||
def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
|
||||
assert self.fd is not None
|
||||
rle4 = self.args[1]
|
||||
data = bytearray()
|
||||
x = 0
|
||||
dest_length = self.state.xsize * self.state.ysize
|
||||
while len(data) < dest_length:
|
||||
pixels = self.fd.read(1)
|
||||
byte = self.fd.read(1)
|
||||
if not pixels or not byte:
|
||||
break
|
||||
num_pixels = pixels[0]
|
||||
if num_pixels:
|
||||
# encoded mode
|
||||
if x + num_pixels > self.state.xsize:
|
||||
# Too much data for row
|
||||
num_pixels = max(0, self.state.xsize - x)
|
||||
if rle4:
|
||||
first_pixel = o8(byte[0] >> 4)
|
||||
second_pixel = o8(byte[0] & 0x0F)
|
||||
for index in range(num_pixels):
|
||||
if index % 2 == 0:
|
||||
data += first_pixel
|
||||
else:
|
||||
data += second_pixel
|
||||
else:
|
||||
data += byte * num_pixels
|
||||
x += num_pixels
|
||||
else:
|
||||
if byte[0] == 0:
|
||||
# end of line
|
||||
while len(data) % self.state.xsize != 0:
|
||||
data += b"\x00"
|
||||
x = 0
|
||||
elif byte[0] == 1:
|
||||
# end of bitmap
|
||||
break
|
||||
elif byte[0] == 2:
|
||||
# delta
|
||||
bytes_read = self.fd.read(2)
|
||||
if len(bytes_read) < 2:
|
||||
break
|
||||
right, up = self.fd.read(2)
|
||||
data += b"\x00" * (right + up * self.state.xsize)
|
||||
x = len(data) % self.state.xsize
|
||||
else:
|
||||
# absolute mode
|
||||
if rle4:
|
||||
# 2 pixels per byte
|
||||
byte_count = byte[0] // 2
|
||||
bytes_read = self.fd.read(byte_count)
|
||||
for byte_read in bytes_read:
|
||||
data += o8(byte_read >> 4)
|
||||
data += o8(byte_read & 0x0F)
|
||||
else:
|
||||
byte_count = byte[0]
|
||||
bytes_read = self.fd.read(byte_count)
|
||||
data += bytes_read
|
||||
if len(bytes_read) < byte_count:
|
||||
break
|
||||
x += byte[0]
|
||||
|
||||
# align to 16-bit word boundary
|
||||
if self.fd.tell() % 2 != 0:
|
||||
self.fd.seek(1, os.SEEK_CUR)
|
||||
rawmode = "L" if self.mode == "L" else "P"
|
||||
self.set_as_raw(bytes(data), rawmode, (0, self.args[-1]))
|
||||
return -1, 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Image plugin for the DIB format (BMP alias)
|
||||
# =============================================================================
|
||||
class DibImageFile(BmpImageFile):
|
||||
format = "DIB"
|
||||
format_description = "Windows Bitmap"
|
||||
|
||||
def _open(self) -> None:
|
||||
self._bitmap()
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
# Write BMP file
|
||||
|
||||
|
||||
SAVE = {
|
||||
"1": ("1", 1, 2),
|
||||
"L": ("L", 8, 256),
|
||||
"P": ("P", 8, 256),
|
||||
"RGB": ("BGR", 24, 0),
|
||||
"RGBA": ("BGRA", 32, 0),
|
||||
}
|
||||
|
||||
|
||||
def _dib_save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
_save(im, fp, filename, False)
|
||||
|
||||
|
||||
def _save(
|
||||
im: Image.Image, fp: IO[bytes], filename: str | bytes, bitmap_header: bool = True
|
||||
) -> None:
|
||||
try:
|
||||
rawmode, bits, colors = SAVE[im.mode]
|
||||
except KeyError as e:
|
||||
msg = f"cannot write mode {im.mode} as BMP"
|
||||
raise OSError(msg) from e
|
||||
|
||||
info = im.encoderinfo
|
||||
|
||||
dpi = info.get("dpi", (96, 96))
|
||||
|
||||
# 1 meter == 39.3701 inches
|
||||
ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi)
|
||||
|
||||
stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
|
||||
header = 40 # or 64 for OS/2 version 2
|
||||
image = stride * im.size[1]
|
||||
|
||||
if im.mode == "1":
|
||||
palette = b"".join(o8(i) * 3 + b"\x00" for i in (0, 255))
|
||||
elif im.mode == "L":
|
||||
palette = b"".join(o8(i) * 3 + b"\x00" for i in range(256))
|
||||
elif im.mode == "P":
|
||||
palette = im.im.getpalette("RGB", "BGRX")
|
||||
colors = len(palette) // 4
|
||||
else:
|
||||
palette = None
|
||||
|
||||
# bitmap header
|
||||
if bitmap_header:
|
||||
offset = 14 + header + colors * 4
|
||||
file_size = offset + image
|
||||
if file_size > 2**32 - 1:
|
||||
msg = "File size is too large for the BMP format"
|
||||
raise ValueError(msg)
|
||||
fp.write(
|
||||
b"BM" # file type (magic)
|
||||
+ o32(file_size) # file size
|
||||
+ o32(0) # reserved
|
||||
+ o32(offset) # image data offset
|
||||
)
|
||||
|
||||
# bitmap info header
|
||||
fp.write(
|
||||
o32(header) # info header size
|
||||
+ o32(im.size[0]) # width
|
||||
+ o32(im.size[1]) # height
|
||||
+ o16(1) # planes
|
||||
+ o16(bits) # depth
|
||||
+ o32(0) # compression (0=uncompressed)
|
||||
+ o32(image) # size of bitmap
|
||||
+ o32(ppm[0]) # resolution
|
||||
+ o32(ppm[1]) # resolution
|
||||
+ o32(colors) # colors used
|
||||
+ o32(colors) # colors important
|
||||
)
|
||||
|
||||
fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
|
||||
|
||||
if palette:
|
||||
fp.write(palette)
|
||||
|
||||
ImageFile._save(
|
||||
im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))]
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
# Registry
|
||||
|
||||
|
||||
Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
|
||||
Image.register_save(BmpImageFile.format, _save)
|
||||
|
||||
Image.register_extension(BmpImageFile.format, ".bmp")
|
||||
|
||||
Image.register_mime(BmpImageFile.format, "image/bmp")
|
||||
|
||||
Image.register_decoder("bmp_rle", BmpRleDecoder)
|
||||
|
||||
Image.register_open(DibImageFile.format, DibImageFile, _dib_accept)
|
||||
Image.register_save(DibImageFile.format, _dib_save)
|
||||
|
||||
Image.register_extension(DibImageFile.format, ".dib")
|
||||
|
||||
Image.register_mime(DibImageFile.format, "image/bmp")
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
#
|
||||
# The Python Imaging Library
|
||||
# $Id$
|
||||
#
|
||||
# BUFR stub adapter
|
||||
#
|
||||
# Copyright (c) 1996-2003 by Fredrik Lundh
|
||||
#
|
||||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import IO
|
||||
|
||||
from . import Image, ImageFile
|
||||
|
||||
_handler = None
|
||||
|
||||
|
||||
def register_handler(handler: ImageFile.StubHandler | None) -> None:
|
||||
"""
|
||||
Install application-specific BUFR image handler.
|
||||
|
||||
:param handler: Handler object.
|
||||
"""
|
||||
global _handler
|
||||
_handler = handler
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Image adapter
|
||||
|
||||
|
||||
def _accept(prefix: bytes) -> bool:
|
||||
return prefix.startswith((b"BUFR", b"ZCZC"))
|
||||
|
||||
|
||||
class BufrStubImageFile(ImageFile.StubImageFile):
|
||||
format = "BUFR"
|
||||
format_description = "BUFR"
|
||||
|
||||
def _open(self) -> None:
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "Not a BUFR file"
|
||||
raise SyntaxError(msg)
|
||||
|
||||
self.fp.seek(-4, os.SEEK_CUR)
|
||||
|
||||
# make something up
|
||||
self._mode = "F"
|
||||
self._size = 1, 1
|
||||
|
||||
loader = self._load()
|
||||
if loader:
|
||||
loader.open(self)
|
||||
|
||||
def _load(self) -> ImageFile.StubHandler | None:
|
||||
return _handler
|
||||
|
||||
|
||||
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
if _handler is None or not hasattr(_handler, "save"):
|
||||
msg = "BUFR save handler not installed"
|
||||
raise OSError(msg)
|
||||
_handler.save(im, fp, filename)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Registry
|
||||
|
||||
Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept)
|
||||
Image.register_save(BufrStubImageFile.format, _save)
|
||||
|
||||
Image.register_extension(BufrStubImageFile.format, ".bufr")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue