Полная переиндексация проекта: все файлы добавлены заново
2
.gitignore
vendored
@ -5,6 +5,8 @@ __pycache__
|
|||||||
db.sqlite3
|
db.sqlite3
|
||||||
media/
|
media/
|
||||||
staticfiles/
|
staticfiles/
|
||||||
|
*/migrations/*.py
|
||||||
|
!*/migrations/__init__.py
|
||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
|
|||||||
28
.idea/.gitignore
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
|
|
||||||
# Django
|
|
||||||
*.pyc
|
|
||||||
*~
|
|
||||||
__pycache__
|
|
||||||
db.sqlite3
|
|
||||||
media/
|
|
||||||
staticfiles/
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
11
.idea/djsite.iml
generated
@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/OneCprogsite" isTestSource="false" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
10
.idea/misc.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Black">
|
|
||||||
<option name="sdkName" value="Python 3.12 (djsite) (2)" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (djsite) (2)" project-jdk-type="Python SDK" />
|
|
||||||
<component name="PyCharmProfessionalAdvertiser">
|
|
||||||
<option name="shown" value="true" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/djsite.iml" filepath="$PROJECT_DIR$/.idea/djsite.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 943 KiB |
|
Before Width: | Height: | Size: 911 KiB |
|
Before Width: | Height: | Size: 548 KiB |
|
Before Width: | Height: | Size: 850 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 943 KiB |
|
Before Width: | Height: | Size: 911 KiB |
|
Before Width: | Height: | Size: 548 KiB |
|
Before Width: | Height: | Size: 850 KiB |
@ -1,26 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-23 12:47
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Competence',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('title', models.CharField(max_length=255)),
|
|
||||||
('content', models.TextField(blank=True)),
|
|
||||||
('photo', models.ImageField(upload_to='photos/%Y/%m/%d/')),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('time_update', models.DateTimeField(auto_now=True)),
|
|
||||||
('is_publiched', models.BooleanField(default=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-24 08:03
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='competence',
|
|
||||||
options={'ordering': ['time_create', 'title'], 'verbose_name': 'Компитенция', 'verbose_name_plural': 'Компитенции'},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='competence',
|
|
||||||
name='content',
|
|
||||||
field=models.TextField(blank=True, verbose_name='Компетенция'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='competence',
|
|
||||||
name='is_publiched',
|
|
||||||
field=models.BooleanField(default=True, verbose_name='Опубликован'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='competence',
|
|
||||||
name='photo',
|
|
||||||
field=models.ImageField(upload_to='photos/%Y/%m/%d/', verbose_name='Фото'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='competence',
|
|
||||||
name='time_create',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Дата создания'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='competence',
|
|
||||||
name='time_update',
|
|
||||||
field=models.DateTimeField(auto_now=True, verbose_name='Дата изменения'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='competence',
|
|
||||||
name='title',
|
|
||||||
field=models.CharField(max_length=255, verbose_name='Программист'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-24 11:54
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0002_alter_competence_options_alter_competence_content_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Recall',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('title', models.CharField(max_length=255, verbose_name='Организация')),
|
|
||||||
('content', models.TextField(blank=True, verbose_name='Отзыв')),
|
|
||||||
('photo', models.ImageField(upload_to='photos/%Y/%m/%d/', verbose_name='Фото')),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
|
||||||
('time_update', models.DateTimeField(auto_now=True, verbose_name='Дата изменения')),
|
|
||||||
('is_published', models.BooleanField(default=True, verbose_name='Опубликован')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Отзыв',
|
|
||||||
'verbose_name_plural': 'Отзывы',
|
|
||||||
'ordering': ['time_create', 'title'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='competence',
|
|
||||||
old_name='is_publiched',
|
|
||||||
new_name='is_published',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-24 12:02
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0003_recall_rename_is_publiched_competence_is_published'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='recall',
|
|
||||||
old_name='photo',
|
|
||||||
new_name='scan',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-24 12:19
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0004_rename_photo_recall_scan'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Recall',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('title', models.CharField(max_length=255, verbose_name='Организация')),
|
|
||||||
('content', models.TextField(blank=True, verbose_name='Отзыв')),
|
|
||||||
('scan', models.ImageField(upload_to='photos/%Y/%m/%d/', verbose_name='Скан')),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
|
||||||
('time_update', models.DateTimeField(auto_now=True, verbose_name='Дата изменения')),
|
|
||||||
('is_published', models.BooleanField(default=True, verbose_name='Опубликован')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Отзыв',
|
|
||||||
'verbose_name_plural': 'Отзывы',
|
|
||||||
'ordering': ['time_create', 'title'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-25 09:18
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0005_auto_20231124_1519'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='recall',
|
|
||||||
name='scan',
|
|
||||||
field=models.ImageField(upload_to='scan/%Y/%m/%d/', verbose_name='Фото'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-25 09:51
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0006_alter_recall_scan'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Solution',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('title', models.CharField(max_length=255, verbose_name='Наименование')),
|
|
||||||
('description', models.TextField(blank=True, verbose_name='Описание')),
|
|
||||||
('implementation', models.TextField(blank=True, verbose_name='Реализация')),
|
|
||||||
('closing', models.TextField(blank=True, verbose_name='Заключение')),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
|
||||||
('time_update', models.DateTimeField(auto_now=True, verbose_name='Дата изменения')),
|
|
||||||
('is_published', models.BooleanField(default=True, verbose_name='Опубликован')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Проекты',
|
|
||||||
'verbose_name_plural': 'Проекты',
|
|
||||||
'ordering': ['time_create', 'title'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2023-11-25 10:45
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0007_solution'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Home',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('title', models.CharField(max_length=255, verbose_name='Наименование')),
|
|
||||||
('content', models.TextField(blank=True, verbose_name='Статья')),
|
|
||||||
('home_image', models.ImageField(upload_to='home_image/%Y/%m/%d/', verbose_name='Фото')),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
|
||||||
('time_update', models.DateTimeField(auto_now=True, verbose_name='Дата изменения')),
|
|
||||||
('is_published', models.BooleanField(default=True, verbose_name='Опубликован')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Главная страница',
|
|
||||||
'verbose_name_plural': 'Главная страница',
|
|
||||||
'ordering': ['time_create', 'title'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2025-11-09 12:00
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0008_home'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CallbackRequest',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=100, verbose_name='Имя')),
|
|
||||||
('phone', models.CharField(max_length=20, verbose_name='Телефон')),
|
|
||||||
('email', models.EmailField(max_length=254, verbose_name='Электронная почта')),
|
|
||||||
('question', models.TextField(verbose_name='Ваш вопрос')),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
|
||||||
('is_processed', models.BooleanField(default=False, verbose_name='Обработано')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Заявка на звонок',
|
|
||||||
'verbose_name_plural': 'Заявки на звонок',
|
|
||||||
'ordering': ['-time_create'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='competence',
|
|
||||||
options={'ordering': ['time_create', 'title'], 'verbose_name': 'Компетенция', 'verbose_name_plural': 'Компетенции'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2025-11-09 12:09
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0009_callbackrequest_alter_competence_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='callbackrequest',
|
|
||||||
name='email',
|
|
||||||
field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='Электронная почта'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='callbackrequest',
|
|
||||||
name='question',
|
|
||||||
field=models.TextField(blank=True, verbose_name='Ваш вопрос'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2025-11-12 11:43
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.utils.timezone
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0010_alter_callbackrequest_email_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Visitor',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('ip_address', models.GenericIPAddressField()),
|
|
||||||
('first_visit', models.DateTimeField(default=django.utils.timezone.now)),
|
|
||||||
('last_visit', models.DateTimeField(default=django.utils.timezone.now)),
|
|
||||||
('visit_count', models.IntegerField(default=1)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'indexes': [models.Index(fields=['ip_address'], name='programmer__ip_addr_2c6dca_idx')],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='PageView',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('url', models.CharField(max_length=500)),
|
|
||||||
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
|
|
||||||
('ip_address', models.GenericIPAddressField()),
|
|
||||||
('user_agent', models.TextField(blank=True)),
|
|
||||||
('referer', models.CharField(blank=True, max_length=500)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'indexes': [models.Index(fields=['url', 'timestamp'], name='programmer__url_9a41b2_idx'), models.Index(fields=['timestamp'], name='programmer__timesta_070072_idx')],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2025-11-14 09:37
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0011_visitor_pageview'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='callbackrequest',
|
|
||||||
name='is_read',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='Прочитано'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 4.2.26 on 2025-11-14 10:36
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0011_visitor_pageview'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='callbackrequest',
|
|
||||||
name='is_read',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='Прочитано'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='callbackrequest',
|
|
||||||
name='notification_sent',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='Уведомление отправлено'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2025-11-14 10:03
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('programmer', '0012_callbackrequest_is_read'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='callbackrequest',
|
|
||||||
name='notification_sent',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='Уведомление отправлено'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1 +0,0 @@
|
|||||||
pip
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
Copyright (c) Django Software Foundation and individual contributors.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
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 Django 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 OWNER 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,290 +0,0 @@
|
|||||||
Django is licensed under the three-clause BSD license; see the file
|
|
||||||
LICENSE for details.
|
|
||||||
|
|
||||||
Django includes code from the Python standard library, which is licensed under
|
|
||||||
the Python license, a permissive open source license. The copyright and license
|
|
||||||
is included below for compliance with Python's terms.
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 2001-present Python Software Foundation; All Rights Reserved
|
|
||||||
|
|
||||||
A. HISTORY OF THE SOFTWARE
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
|
||||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
|
||||||
as a successor of a language called ABC. Guido remains Python's
|
|
||||||
principal author, although it includes many contributions from others.
|
|
||||||
|
|
||||||
In 1995, Guido continued his work on Python at the Corporation for
|
|
||||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
|
||||||
in Reston, Virginia where he released several versions of the
|
|
||||||
software.
|
|
||||||
|
|
||||||
In May 2000, Guido and the Python core development team moved to
|
|
||||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
|
||||||
year, the PythonLabs team moved to Digital Creations, which became
|
|
||||||
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
|
|
||||||
https://www.python.org/psf/) was formed, a non-profit organization
|
|
||||||
created specifically to own Python-related Intellectual Property.
|
|
||||||
Zope Corporation was a sponsoring member of the PSF.
|
|
||||||
|
|
||||||
All Python releases are Open Source (see http://www.opensource.org for
|
|
||||||
the Open Source Definition). Historically, most, but not all, Python
|
|
||||||
releases have also been GPL-compatible; the table below summarizes
|
|
||||||
the various releases.
|
|
||||||
|
|
||||||
Release Derived Year Owner GPL-
|
|
||||||
from compatible? (1)
|
|
||||||
|
|
||||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
|
||||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
|
||||||
1.6 1.5.2 2000 CNRI no
|
|
||||||
2.0 1.6 2000 BeOpen.com no
|
|
||||||
1.6.1 1.6 2001 CNRI yes (2)
|
|
||||||
2.1 2.0+1.6.1 2001 PSF no
|
|
||||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
|
||||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
|
||||||
2.1.2 2.1.1 2002 PSF yes
|
|
||||||
2.1.3 2.1.2 2002 PSF yes
|
|
||||||
2.2 and above 2.1.1 2001-now PSF yes
|
|
||||||
|
|
||||||
Footnotes:
|
|
||||||
|
|
||||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
|
||||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
|
||||||
a modified version without making your changes open source. The
|
|
||||||
GPL-compatible licenses make it possible to combine Python with
|
|
||||||
other software that is released under the GPL; the others don't.
|
|
||||||
|
|
||||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
|
||||||
because its license has a choice of law clause. According to
|
|
||||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
|
||||||
is "not incompatible" with the GPL.
|
|
||||||
|
|
||||||
Thanks to the many outside volunteers who have worked under Guido's
|
|
||||||
direction to make these releases possible.
|
|
||||||
|
|
||||||
|
|
||||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
|
||||||
===============================================================
|
|
||||||
|
|
||||||
Python software and documentation are licensed under the
|
|
||||||
Python Software Foundation License Version 2.
|
|
||||||
|
|
||||||
Starting with Python 3.8.6, examples, recipes, and other code in
|
|
||||||
the documentation are dual licensed under the PSF License Version 2
|
|
||||||
and the Zero-Clause BSD license.
|
|
||||||
|
|
||||||
Some software incorporated into Python is under different licenses.
|
|
||||||
The licenses are listed with code falling under that license.
|
|
||||||
|
|
||||||
|
|
||||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
|
||||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
|
||||||
otherwise using this software ("Python") in source or binary form and
|
|
||||||
its associated documentation.
|
|
||||||
|
|
||||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
|
||||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
|
||||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
|
||||||
distribute, and otherwise use Python alone or in any derivative version,
|
|
||||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
|
||||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
|
||||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
|
|
||||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
|
||||||
prepared by Licensee.
|
|
||||||
|
|
||||||
3. In the event Licensee prepares a derivative work that is based on
|
|
||||||
or incorporates Python or any part thereof, and wants to make
|
|
||||||
the derivative work available to others as provided herein, then
|
|
||||||
Licensee hereby agrees to include in any such work a brief summary of
|
|
||||||
the changes made to Python.
|
|
||||||
|
|
||||||
4. PSF is making Python available to Licensee on an "AS IS"
|
|
||||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
||||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
|
||||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
||||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
|
||||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
|
||||||
|
|
||||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
|
||||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
|
||||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
|
||||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
|
||||||
|
|
||||||
6. This License Agreement will automatically terminate upon a material
|
|
||||||
breach of its terms and conditions.
|
|
||||||
|
|
||||||
7. Nothing in this License Agreement shall be deemed to create any
|
|
||||||
relationship of agency, partnership, or joint venture between PSF and
|
|
||||||
Licensee. This License Agreement does not grant permission to use PSF
|
|
||||||
trademarks or trade name in a trademark sense to endorse or promote
|
|
||||||
products or services of Licensee, or any third party.
|
|
||||||
|
|
||||||
8. By copying, installing or otherwise using Python, Licensee
|
|
||||||
agrees to be bound by the terms and conditions of this License
|
|
||||||
Agreement.
|
|
||||||
|
|
||||||
|
|
||||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
|
||||||
|
|
||||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
|
||||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
|
||||||
Individual or Organization ("Licensee") accessing and otherwise using
|
|
||||||
this software in source or binary form and its associated
|
|
||||||
documentation ("the Software").
|
|
||||||
|
|
||||||
2. Subject to the terms and conditions of this BeOpen Python License
|
|
||||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
|
||||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
|
||||||
and/or display publicly, prepare derivative works, distribute, and
|
|
||||||
otherwise use the Software alone or in any derivative version,
|
|
||||||
provided, however, that the BeOpen Python License is retained in the
|
|
||||||
Software, alone or in any derivative version prepared by Licensee.
|
|
||||||
|
|
||||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
|
||||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
||||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
|
||||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
||||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
|
||||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
|
||||||
|
|
||||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
|
||||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
|
||||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
|
||||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
|
||||||
|
|
||||||
5. This License Agreement will automatically terminate upon a material
|
|
||||||
breach of its terms and conditions.
|
|
||||||
|
|
||||||
6. This License Agreement shall be governed by and interpreted in all
|
|
||||||
respects by the law of the State of California, excluding conflict of
|
|
||||||
law provisions. Nothing in this License Agreement shall be deemed to
|
|
||||||
create any relationship of agency, partnership, or joint venture
|
|
||||||
between BeOpen and Licensee. This License Agreement does not grant
|
|
||||||
permission to use BeOpen trademarks or trade names in a trademark
|
|
||||||
sense to endorse or promote products or services of Licensee, or any
|
|
||||||
third party. As an exception, the "BeOpen Python" logos available at
|
|
||||||
http://www.pythonlabs.com/logos.html may be used according to the
|
|
||||||
permissions granted on that web page.
|
|
||||||
|
|
||||||
7. By copying, installing or otherwise using the software, Licensee
|
|
||||||
agrees to be bound by the terms and conditions of this License
|
|
||||||
Agreement.
|
|
||||||
|
|
||||||
|
|
||||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
|
||||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
|
||||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
|
||||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
|
||||||
source or binary form and its associated documentation.
|
|
||||||
|
|
||||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
|
||||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
|
||||||
license to reproduce, analyze, test, perform and/or display publicly,
|
|
||||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
|
||||||
alone or in any derivative version, provided, however, that CNRI's
|
|
||||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
|
||||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
|
||||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
|
||||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
|
||||||
Agreement, Licensee may substitute the following text (omitting the
|
|
||||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
|
||||||
conditions in CNRI's License Agreement. This Agreement together with
|
|
||||||
Python 1.6.1 may be located on the internet using the following
|
|
||||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
|
||||||
Agreement may also be obtained from a proxy server on the internet
|
|
||||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
|
||||||
|
|
||||||
3. In the event Licensee prepares a derivative work that is based on
|
|
||||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
|
||||||
the derivative work available to others as provided herein, then
|
|
||||||
Licensee hereby agrees to include in any such work a brief summary of
|
|
||||||
the changes made to Python 1.6.1.
|
|
||||||
|
|
||||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
|
||||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
|
||||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
|
||||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
||||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
|
||||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
|
||||||
|
|
||||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
|
||||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
|
||||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
|
||||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
|
||||||
|
|
||||||
6. This License Agreement will automatically terminate upon a material
|
|
||||||
breach of its terms and conditions.
|
|
||||||
|
|
||||||
7. This License Agreement shall be governed by the federal
|
|
||||||
intellectual property law of the United States, including without
|
|
||||||
limitation the federal copyright law, and, to the extent such
|
|
||||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
|
||||||
Virginia, excluding Virginia's conflict of law provisions.
|
|
||||||
Notwithstanding the foregoing, with regard to derivative works based
|
|
||||||
on Python 1.6.1 that incorporate non-separable material that was
|
|
||||||
previously distributed under the GNU General Public License (GPL), the
|
|
||||||
law of the Commonwealth of Virginia shall govern this License
|
|
||||||
Agreement only as to issues arising under or with respect to
|
|
||||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
|
||||||
License Agreement shall be deemed to create any relationship of
|
|
||||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
|
||||||
License Agreement does not grant permission to use CNRI trademarks or
|
|
||||||
trade name in a trademark sense to endorse or promote products or
|
|
||||||
services of Licensee, or any third party.
|
|
||||||
|
|
||||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
|
||||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
|
||||||
bound by the terms and conditions of this License Agreement.
|
|
||||||
|
|
||||||
ACCEPT
|
|
||||||
|
|
||||||
|
|
||||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
|
||||||
The Netherlands. All rights reserved.
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software and its
|
|
||||||
documentation for any purpose and without fee is hereby granted,
|
|
||||||
provided that the above copyright notice appear in all copies and that
|
|
||||||
both that copyright notice and this permission notice appear in
|
|
||||||
supporting documentation, and that the name of Stichting Mathematisch
|
|
||||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
|
||||||
distribution of the software without specific, written prior
|
|
||||||
permission.
|
|
||||||
|
|
||||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
|
||||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
|
||||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
||||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
Metadata-Version: 2.1
|
|
||||||
Name: Django
|
|
||||||
Version: 4.2.7
|
|
||||||
Summary: A high-level Python web framework that encourages rapid development and clean, pragmatic design.
|
|
||||||
Home-page: https://www.djangoproject.com/
|
|
||||||
Author: Django Software Foundation
|
|
||||||
Author-email: foundation@djangoproject.com
|
|
||||||
License: BSD-3-Clause
|
|
||||||
Project-URL: Documentation, https://docs.djangoproject.com/
|
|
||||||
Project-URL: Release notes, https://docs.djangoproject.com/en/stable/releases/
|
|
||||||
Project-URL: Funding, https://www.djangoproject.com/fundraising/
|
|
||||||
Project-URL: Source, https://github.com/django/django
|
|
||||||
Project-URL: Tracker, https://code.djangoproject.com/
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Development Status :: 5 - Production/Stable
|
|
||||||
Classifier: Environment :: Web Environment
|
|
||||||
Classifier: Framework :: Django
|
|
||||||
Classifier: Intended Audience :: Developers
|
|
||||||
Classifier: License :: OSI Approved :: BSD License
|
|
||||||
Classifier: Operating System :: OS Independent
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3 :: Only
|
|
||||||
Classifier: Programming Language :: Python :: 3.8
|
|
||||||
Classifier: Programming Language :: Python :: 3.9
|
|
||||||
Classifier: Programming Language :: Python :: 3.10
|
|
||||||
Classifier: Programming Language :: Python :: 3.11
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
|
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
||||||
Requires-Python: >=3.8
|
|
||||||
License-File: LICENSE
|
|
||||||
License-File: LICENSE.python
|
|
||||||
License-File: AUTHORS
|
|
||||||
Requires-Dist: asgiref (<4,>=3.6.0)
|
|
||||||
Requires-Dist: sqlparse (>=0.3.1)
|
|
||||||
Requires-Dist: backports.zoneinfo ; python_version < "3.9"
|
|
||||||
Requires-Dist: tzdata ; sys_platform == "win32"
|
|
||||||
Provides-Extra: argon2
|
|
||||||
Requires-Dist: argon2-cffi (>=19.1.0) ; extra == 'argon2'
|
|
||||||
Provides-Extra: bcrypt
|
|
||||||
Requires-Dist: bcrypt ; extra == 'bcrypt'
|
|
||||||
|
|
||||||
======
|
|
||||||
Django
|
|
||||||
======
|
|
||||||
|
|
||||||
Django is a high-level Python web framework that encourages rapid development
|
|
||||||
and clean, pragmatic design. Thanks for checking it out.
|
|
||||||
|
|
||||||
All documentation is in the "``docs``" directory and online at
|
|
||||||
https://docs.djangoproject.com/en/stable/. If you're just getting started,
|
|
||||||
here's how we recommend you read the docs:
|
|
||||||
|
|
||||||
* First, read ``docs/intro/install.txt`` for instructions on installing Django.
|
|
||||||
|
|
||||||
* Next, work through the tutorials in order (``docs/intro/tutorial01.txt``,
|
|
||||||
``docs/intro/tutorial02.txt``, etc.).
|
|
||||||
|
|
||||||
* If you want to set up an actual deployment server, read
|
|
||||||
``docs/howto/deployment/index.txt`` for instructions.
|
|
||||||
|
|
||||||
* You'll probably want to read through the topical guides (in ``docs/topics``)
|
|
||||||
next; from there you can jump to the HOWTOs (in ``docs/howto``) for specific
|
|
||||||
problems, and check out the reference (``docs/ref``) for gory details.
|
|
||||||
|
|
||||||
* See ``docs/README`` for instructions on building an HTML version of the docs.
|
|
||||||
|
|
||||||
Docs are updated rigorously. If you find any problems in the docs, or think
|
|
||||||
they should be clarified in any way, please take 30 seconds to fill out a
|
|
||||||
ticket here: https://code.djangoproject.com/newticket
|
|
||||||
|
|
||||||
To get more help:
|
|
||||||
|
|
||||||
* Join the ``#django`` channel on ``irc.libera.chat``. Lots of helpful people
|
|
||||||
hang out there. See https://web.libera.chat if you're new to IRC.
|
|
||||||
|
|
||||||
* Join the django-users mailing list, or read the archives, at
|
|
||||||
https://groups.google.com/group/django-users.
|
|
||||||
|
|
||||||
To contribute to Django:
|
|
||||||
|
|
||||||
* Check out https://docs.djangoproject.com/en/dev/internals/contributing/ for
|
|
||||||
information about getting involved.
|
|
||||||
|
|
||||||
To run Django's test suite:
|
|
||||||
|
|
||||||
* Follow the instructions in the "Unit tests" section of
|
|
||||||
``docs/internals/contributing/writing-code/unit-tests.txt``, published online at
|
|
||||||
https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/unit-tests/#running-the-unit-tests
|
|
||||||
|
|
||||||
Supporting the Development of Django
|
|
||||||
====================================
|
|
||||||
|
|
||||||
Django's development depends on your contributions.
|
|
||||||
|
|
||||||
If you depend on Django, remember to support the Django Software Foundation: https://www.djangoproject.com/fundraising/
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
Wheel-Version: 1.0
|
|
||||||
Generator: bdist_wheel (0.37.1)
|
|
||||||
Root-Is-Purelib: true
|
|
||||||
Tag: py3-none-any
|
|
||||||
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
[console_scripts]
|
|
||||||
django-admin = django.core.management:execute_from_command_line
|
|
||||||
|
|
||||||
@ -1 +0,0 @@
|
|||||||
django
|
|
||||||
@ -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 . import FontFile, Image
|
|
||||||
|
|
||||||
bdf_slant = {
|
|
||||||
"R": "Roman",
|
|
||||||
"I": "Italic",
|
|
||||||
"O": "Oblique",
|
|
||||||
"RI": "Reverse Italic",
|
|
||||||
"RO": "Reverse Oblique",
|
|
||||||
"OT": "Other",
|
|
||||||
}
|
|
||||||
|
|
||||||
bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"}
|
|
||||||
|
|
||||||
|
|
||||||
def bdf_char(f):
|
|
||||||
# skip to STARTCHAR
|
|
||||||
while True:
|
|
||||||
s = f.readline()
|
|
||||||
if not s:
|
|
||||||
return None
|
|
||||||
if s[:9] == b"STARTCHAR":
|
|
||||||
break
|
|
||||||
id = s[9:].strip().decode("ascii")
|
|
||||||
|
|
||||||
# load symbol properties
|
|
||||||
props = {}
|
|
||||||
while True:
|
|
||||||
s = f.readline()
|
|
||||||
if not s or s[:6] == b"BITMAP":
|
|
||||||
break
|
|
||||||
i = s.find(b" ")
|
|
||||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
|
||||||
|
|
||||||
# load bitmap
|
|
||||||
bitmap = []
|
|
||||||
while True:
|
|
||||||
s = f.readline()
|
|
||||||
if not s or s[:7] == b"ENDCHAR":
|
|
||||||
break
|
|
||||||
bitmap.append(s[:-1])
|
|
||||||
bitmap = b"".join(bitmap)
|
|
||||||
|
|
||||||
# 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):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
s = fp.readline()
|
|
||||||
if s[:13] != 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[:13] == 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,474 +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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import struct
|
|
||||||
from enum import IntEnum
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
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):
|
|
||||||
return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3
|
|
||||||
|
|
||||||
|
|
||||||
def decode_dxt1(data, alpha=False):
|
|
||||||
"""
|
|
||||||
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 in range(blocks):
|
|
||||||
# Decode next 8-byte block.
|
|
||||||
idx = block * 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):
|
|
||||||
"""
|
|
||||||
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 in range(blocks):
|
|
||||||
idx = block * 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):
|
|
||||||
"""
|
|
||||||
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 in range(blocks):
|
|
||||||
idx = block * 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):
|
|
||||||
return prefix[:4] in (b"BLP1", b"BLP2")
|
|
||||||
|
|
||||||
|
|
||||||
class BlpImageFile(ImageFile.ImageFile):
|
|
||||||
"""
|
|
||||||
Blizzard Mipmap Format
|
|
||||||
"""
|
|
||||||
|
|
||||||
format = "BLP"
|
|
||||||
format_description = "Blizzard Mipmap Format"
|
|
||||||
|
|
||||||
def _open(self):
|
|
||||||
self.magic = self.fp.read(4)
|
|
||||||
|
|
||||||
self.fp.seek(5, os.SEEK_CUR)
|
|
||||||
(self._blp_alpha_depth,) = struct.unpack("<b", self.fp.read(1))
|
|
||||||
|
|
||||||
self.fp.seek(2, os.SEEK_CUR)
|
|
||||||
self._size = struct.unpack("<II", self.fp.read(8))
|
|
||||||
|
|
||||||
if self.magic in (b"BLP1", b"BLP2"):
|
|
||||||
decoder = self.magic.decode()
|
|
||||||
else:
|
|
||||||
msg = f"Bad BLP magic {repr(self.magic)}"
|
|
||||||
raise BLPFormatError(msg)
|
|
||||||
|
|
||||||
self._mode = "RGBA" if self._blp_alpha_depth else "RGB"
|
|
||||||
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
|
|
||||||
|
|
||||||
|
|
||||||
class _BLPBaseDecoder(ImageFile.PyDecoder):
|
|
||||||
_pulls_fd = True
|
|
||||||
|
|
||||||
def decode(self, buffer):
|
|
||||||
try:
|
|
||||||
self._read_blp_header()
|
|
||||||
self._load()
|
|
||||||
except struct.error as e:
|
|
||||||
msg = "Truncated BLP file"
|
|
||||||
raise OSError(msg) from e
|
|
||||||
return -1, 0
|
|
||||||
|
|
||||||
def _read_blp_header(self):
|
|
||||||
self.fd.seek(4)
|
|
||||||
(self._blp_compression,) = struct.unpack("<i", self._safe_read(4))
|
|
||||||
|
|
||||||
(self._blp_encoding,) = struct.unpack("<b", self._safe_read(1))
|
|
||||||
(self._blp_alpha_depth,) = struct.unpack("<b", self._safe_read(1))
|
|
||||||
(self._blp_alpha_encoding,) = struct.unpack("<b", self._safe_read(1))
|
|
||||||
self.fd.seek(1, os.SEEK_CUR) # mips
|
|
||||||
|
|
||||||
self.size = struct.unpack("<II", self._safe_read(8))
|
|
||||||
|
|
||||||
if isinstance(self, BLP1Decoder):
|
|
||||||
# Only present for BLP1
|
|
||||||
(self._blp_encoding,) = struct.unpack("<i", self._safe_read(4))
|
|
||||||
self.fd.seek(4, os.SEEK_CUR) # subtype
|
|
||||||
|
|
||||||
self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
|
|
||||||
self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))
|
|
||||||
|
|
||||||
def _safe_read(self, length):
|
|
||||||
return ImageFile._safe_read(self.fd, length)
|
|
||||||
|
|
||||||
def _read_palette(self):
|
|
||||||
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):
|
|
||||||
data = bytearray()
|
|
||||||
_data = BytesIO(self._safe_read(self._blp_lengths[0]))
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
(offset,) = struct.unpack("<B", _data.read(1))
|
|
||||||
except struct.error:
|
|
||||||
break
|
|
||||||
b, g, r, a = palette[offset]
|
|
||||||
d = (r, g, b)
|
|
||||||
if self._blp_alpha_depth:
|
|
||||||
d += (a,)
|
|
||||||
data.extend(d)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class BLP1Decoder(_BLPBaseDecoder):
|
|
||||||
def _load(self):
|
|
||||||
if self._blp_compression == Format.JPEG:
|
|
||||||
self._decode_jpeg_stream()
|
|
||||||
|
|
||||||
elif self._blp_compression == 1:
|
|
||||||
if self._blp_encoding in (4, 5):
|
|
||||||
palette = self._read_palette()
|
|
||||||
data = self._read_bgra(palette)
|
|
||||||
self.set_as_raw(bytes(data))
|
|
||||||
else:
|
|
||||||
msg = f"Unsupported BLP encoding {repr(self._blp_encoding)}"
|
|
||||||
raise BLPFormatError(msg)
|
|
||||||
else:
|
|
||||||
msg = f"Unsupported BLP compression {repr(self._blp_encoding)}"
|
|
||||||
raise BLPFormatError(msg)
|
|
||||||
|
|
||||||
def _decode_jpeg_stream(self):
|
|
||||||
from .JpegImagePlugin import JpegImageFile
|
|
||||||
|
|
||||||
(jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
|
|
||||||
jpeg_header = self._safe_read(jpeg_header_size)
|
|
||||||
self._safe_read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
|
|
||||||
data = self._safe_read(self._blp_lengths[0])
|
|
||||||
data = jpeg_header + data
|
|
||||||
data = BytesIO(data)
|
|
||||||
image = JpegImageFile(data)
|
|
||||||
Image._decompression_bomb_check(image.size)
|
|
||||||
if image.mode == "CMYK":
|
|
||||||
decoder_name, extents, offset, args = image.tile[0]
|
|
||||||
image.tile = [(decoder_name, extents, offset, (args[0], "CMYK"))]
|
|
||||||
r, g, b = image.convert("RGB").split()
|
|
||||||
image = Image.merge("RGB", (b, g, r))
|
|
||||||
self.set_as_raw(image.tobytes())
|
|
||||||
|
|
||||||
|
|
||||||
class BLP2Decoder(_BLPBaseDecoder):
|
|
||||||
def _load(self):
|
|
||||||
palette = self._read_palette()
|
|
||||||
|
|
||||||
self.fd.seek(self._blp_offsets[0])
|
|
||||||
|
|
||||||
if self._blp_compression == 1:
|
|
||||||
# Uncompressed or DirectX compression
|
|
||||||
|
|
||||||
if self._blp_encoding == Encoding.UNCOMPRESSED:
|
|
||||||
data = self._read_bgra(palette)
|
|
||||||
|
|
||||||
elif self._blp_encoding == Encoding.DXT:
|
|
||||||
data = bytearray()
|
|
||||||
if self._blp_alpha_encoding == AlphaEncoding.DXT1:
|
|
||||||
linesize = (self.size[0] + 3) // 4 * 8
|
|
||||||
for yb in range((self.size[1] + 3) // 4):
|
|
||||||
for d in decode_dxt1(
|
|
||||||
self._safe_read(linesize), alpha=bool(self._blp_alpha_depth)
|
|
||||||
):
|
|
||||||
data += d
|
|
||||||
|
|
||||||
elif self._blp_alpha_encoding == AlphaEncoding.DXT3:
|
|
||||||
linesize = (self.size[0] + 3) // 4 * 16
|
|
||||||
for yb in range((self.size[1] + 3) // 4):
|
|
||||||
for d in decode_dxt3(self._safe_read(linesize)):
|
|
||||||
data += d
|
|
||||||
|
|
||||||
elif self._blp_alpha_encoding == AlphaEncoding.DXT5:
|
|
||||||
linesize = (self.size[0] + 3) // 4 * 16
|
|
||||||
for yb in range((self.size[1] + 3) // 4):
|
|
||||||
for d in decode_dxt5(self._safe_read(linesize)):
|
|
||||||
data += d
|
|
||||||
else:
|
|
||||||
msg = f"Unsupported alpha encoding {repr(self._blp_alpha_encoding)}"
|
|
||||||
raise BLPFormatError(msg)
|
|
||||||
else:
|
|
||||||
msg = f"Unknown BLP encoding {repr(self._blp_encoding)}"
|
|
||||||
raise BLPFormatError(msg)
|
|
||||||
|
|
||||||
else:
|
|
||||||
msg = f"Unknown BLP compression {repr(self._blp_compression)}"
|
|
||||||
raise BLPFormatError(msg)
|
|
||||||
|
|
||||||
self.set_as_raw(bytes(data))
|
|
||||||
|
|
||||||
|
|
||||||
class BLPEncoder(ImageFile.PyEncoder):
|
|
||||||
_pushes_fd = True
|
|
||||||
|
|
||||||
def _write_palette(self):
|
|
||||||
data = b""
|
|
||||||
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):
|
|
||||||
palette_data = self._write_palette()
|
|
||||||
|
|
||||||
offset = 20 + 16 * 4 * 2 + len(palette_data)
|
|
||||||
data = struct.pack("<16I", offset, *((0,) * 15))
|
|
||||||
|
|
||||||
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, fp, filename):
|
|
||||||
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)
|
|
||||||
|
|
||||||
fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression
|
|
||||||
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
|
|
||||||
fp.write(struct.pack("<b", 1 if im.palette.mode == "RGBA" else 0))
|
|
||||||
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, [("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,471 +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.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
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"),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
|
||||||
return prefix[:2] == b"BM"
|
|
||||||
|
|
||||||
|
|
||||||
def _dib_accept(prefix):
|
|
||||||
return i32(prefix) in [12, 40, 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=0, offset=0):
|
|
||||||
"""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 = {"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
|
|
||||||
header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)
|
|
||||||
|
|
||||||
# -------------------------------------------------- IBM OS/2 Bitmap v1
|
|
||||||
# ----- This format has different offsets because of width/height types
|
|
||||||
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.RAW
|
|
||||||
file_info["palette_padding"] = 3
|
|
||||||
|
|
||||||
# --------------------------------------------- Windows Bitmap v2 to v5
|
|
||||||
# v3, OS/2 v2, v4, v5
|
|
||||||
elif file_info["header_size"] in (40, 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
|
|
||||||
self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"])
|
|
||||||
if file_info["compression"] == self.BITFIELDS:
|
|
||||||
if len(header_data) >= 52:
|
|
||||||
for idx, mask in enumerate(
|
|
||||||
["r_mask", "g_mask", "b_mask", "a_mask"]
|
|
||||||
):
|
|
||||||
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 ["r_mask", "g_mask", "b_mask"]:
|
|
||||||
file_info[mask] = i32(read(4))
|
|
||||||
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
|
|
||||||
self._size = file_info["width"], file_info["height"]
|
|
||||||
|
|
||||||
# ------- If color count was not found in the header, compute from bits
|
|
||||||
file_info["colors"] = (
|
|
||||||
file_info["colors"]
|
|
||||||
if file_info.get("colors", 0)
|
|
||||||
else (1 << file_info["bits"])
|
|
||||||
)
|
|
||||||
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"], (None, None))
|
|
||||||
if self.mode is None:
|
|
||||||
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.BITFIELDS:
|
|
||||||
SUPPORTED = {
|
|
||||||
32: [
|
|
||||||
(0xFF0000, 0xFF00, 0xFF, 0x0),
|
|
||||||
(0xFF000000, 0xFF0000, 0xFF00, 0x0),
|
|
||||||
(0xFF000000, 0xFF0000, 0xFF00, 0xFF),
|
|
||||||
(0xFF, 0xFF00, 0xFF0000, 0xFF000000),
|
|
||||||
(0xFF0000, 0xFF00, 0xFF, 0xFF000000),
|
|
||||||
(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, 0xFF0000, 0xFF00, 0xFF)): "ABGR",
|
|
||||||
(32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
|
|
||||||
(32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
|
|
||||||
(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"]]
|
|
||||||
):
|
|
||||||
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"]]
|
|
||||||
):
|
|
||||||
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.RAW:
|
|
||||||
if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset
|
|
||||||
raw_mode, self._mode = "BGRA", "RGBA"
|
|
||||||
elif file_info["compression"] in (self.RLE8, self.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:
|
|
||||||
padding = file_info["palette_padding"]
|
|
||||||
palette = read(padding * file_info["colors"])
|
|
||||||
greyscale = True
|
|
||||||
indices = (
|
|
||||||
(0, 255)
|
|
||||||
if file_info["colors"] == 2
|
|
||||||
else list(range(file_info["colors"]))
|
|
||||||
)
|
|
||||||
|
|
||||||
# ----------------- Check if greyscale and ignore palette if so
|
|
||||||
for ind, val in enumerate(indices):
|
|
||||||
rgb = palette[ind * padding : ind * padding + 3]
|
|
||||||
if rgb != o8(val) * 3:
|
|
||||||
greyscale = False
|
|
||||||
|
|
||||||
# ------- If all colors are grey, white or black, ditch palette
|
|
||||||
if greyscale:
|
|
||||||
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 = [raw_mode]
|
|
||||||
if decoder_name == "bmp_rle":
|
|
||||||
args.append(file_info["compression"] == self.RLE4)
|
|
||||||
else:
|
|
||||||
args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3))
|
|
||||||
args.append(file_info["direction"])
|
|
||||||
self.tile = [
|
|
||||||
(
|
|
||||||
decoder_name,
|
|
||||||
(0, 0, file_info["width"], file_info["height"]),
|
|
||||||
offset or self.fp.tell(),
|
|
||||||
tuple(args),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
def _open(self):
|
|
||||||
"""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):
|
|
||||||
rle4 = self.args[1]
|
|
||||||
data = bytearray()
|
|
||||||
x = 0
|
|
||||||
while len(data) < self.state.xsize * self.state.ysize:
|
|
||||||
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):
|
|
||||||
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, fp, filename):
|
|
||||||
_save(im, fp, filename, False)
|
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename, bitmap_header=True):
|
|
||||||
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(map(lambda x: int(x * 39.3701 + 0.5), 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) * 4 for i in (0, 255))
|
|
||||||
elif im.mode == "L":
|
|
||||||
palette = b"".join(o8(i) * 4 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, [("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,73 +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 . import Image, ImageFile
|
|
||||||
|
|
||||||
_handler = None
|
|
||||||
|
|
||||||
|
|
||||||
def register_handler(handler):
|
|
||||||
"""
|
|
||||||
Install application-specific BUFR image handler.
|
|
||||||
|
|
||||||
:param handler: Handler object.
|
|
||||||
"""
|
|
||||||
global _handler
|
|
||||||
_handler = handler
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
# Image adapter
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
|
||||||
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"
|
|
||||||
|
|
||||||
|
|
||||||
class BufrStubImageFile(ImageFile.StubImageFile):
|
|
||||||
format = "BUFR"
|
|
||||||
format_description = "BUFR"
|
|
||||||
|
|
||||||
def _open(self):
|
|
||||||
offset = self.fp.tell()
|
|
||||||
|
|
||||||
if not _accept(self.fp.read(4)):
|
|
||||||
msg = "Not a BUFR file"
|
|
||||||
raise SyntaxError(msg)
|
|
||||||
|
|
||||||
self.fp.seek(offset)
|
|
||||||
|
|
||||||
# make something up
|
|
||||||
self._mode = "F"
|
|
||||||
self._size = 1, 1
|
|
||||||
|
|
||||||
loader = self._load()
|
|
||||||
if loader:
|
|
||||||
loader.open(self)
|
|
||||||
|
|
||||||
def _load(self):
|
|
||||||
return _handler
|
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
|
||||||
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")
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
#
|
|
||||||
# The Python Imaging Library.
|
|
||||||
# $Id$
|
|
||||||
#
|
|
||||||
# a class to read from a container file
|
|
||||||
#
|
|
||||||
# History:
|
|
||||||
# 1995-06-18 fl Created
|
|
||||||
# 1995-09-07 fl Added readline(), readlines()
|
|
||||||
#
|
|
||||||
# Copyright (c) 1997-2001 by Secret Labs AB
|
|
||||||
# Copyright (c) 1995 by Fredrik Lundh
|
|
||||||
#
|
|
||||||
# See the README file for information on usage and redistribution.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
import io
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerIO:
|
|
||||||
"""
|
|
||||||
A file object that provides read access to a part of an existing
|
|
||||||
file (for example a TAR file).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, file, offset, length):
|
|
||||||
"""
|
|
||||||
Create file object.
|
|
||||||
|
|
||||||
:param file: Existing file.
|
|
||||||
:param offset: Start of region, in bytes.
|
|
||||||
:param length: Size of region, in bytes.
|
|
||||||
"""
|
|
||||||
self.fh = file
|
|
||||||
self.pos = 0
|
|
||||||
self.offset = offset
|
|
||||||
self.length = length
|
|
||||||
self.fh.seek(offset)
|
|
||||||
|
|
||||||
##
|
|
||||||
# Always false.
|
|
||||||
|
|
||||||
def isatty(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def seek(self, offset, mode=io.SEEK_SET):
|
|
||||||
"""
|
|
||||||
Move file pointer.
|
|
||||||
|
|
||||||
:param offset: Offset in bytes.
|
|
||||||
:param mode: Starting position. Use 0 for beginning of region, 1
|
|
||||||
for current offset, and 2 for end of region. You cannot move
|
|
||||||
the pointer outside the defined region.
|
|
||||||
"""
|
|
||||||
if mode == 1:
|
|
||||||
self.pos = self.pos + offset
|
|
||||||
elif mode == 2:
|
|
||||||
self.pos = self.length + offset
|
|
||||||
else:
|
|
||||||
self.pos = offset
|
|
||||||
# clamp
|
|
||||||
self.pos = max(0, min(self.pos, self.length))
|
|
||||||
self.fh.seek(self.offset + self.pos)
|
|
||||||
|
|
||||||
def tell(self):
|
|
||||||
"""
|
|
||||||
Get current file pointer.
|
|
||||||
|
|
||||||
:returns: Offset from start of region, in bytes.
|
|
||||||
"""
|
|
||||||
return self.pos
|
|
||||||
|
|
||||||
def read(self, n=0):
|
|
||||||
"""
|
|
||||||
Read data.
|
|
||||||
|
|
||||||
:param n: Number of bytes to read. If omitted or zero,
|
|
||||||
read until end of region.
|
|
||||||
:returns: An 8-bit string.
|
|
||||||
"""
|
|
||||||
if n:
|
|
||||||
n = min(n, self.length - self.pos)
|
|
||||||
else:
|
|
||||||
n = self.length - self.pos
|
|
||||||
if not n: # EOF
|
|
||||||
return b"" if "b" in self.fh.mode else ""
|
|
||||||
self.pos = self.pos + n
|
|
||||||
return self.fh.read(n)
|
|
||||||
|
|
||||||
def readline(self):
|
|
||||||
"""
|
|
||||||
Read a line of text.
|
|
||||||
|
|
||||||
:returns: An 8-bit string.
|
|
||||||
"""
|
|
||||||
s = b"" if "b" in self.fh.mode else ""
|
|
||||||
newline_character = b"\n" if "b" in self.fh.mode else "\n"
|
|
||||||
while True:
|
|
||||||
c = self.read(1)
|
|
||||||
if not c:
|
|
||||||
break
|
|
||||||
s = s + c
|
|
||||||
if c == newline_character:
|
|
||||||
break
|
|
||||||
return s
|
|
||||||
|
|
||||||
def readlines(self):
|
|
||||||
"""
|
|
||||||
Read multiple lines of text.
|
|
||||||
|
|
||||||
:returns: A list of 8-bit strings.
|
|
||||||
"""
|
|
||||||
lines = []
|
|
||||||
while True:
|
|
||||||
s = self.readline()
|
|
||||||
if not s:
|
|
||||||
break
|
|
||||||
lines.append(s)
|
|
||||||
return lines
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
#
|
|
||||||
# The Python Imaging Library.
|
|
||||||
# $Id$
|
|
||||||
#
|
|
||||||
# Windows Cursor support for PIL
|
|
||||||
#
|
|
||||||
# notes:
|
|
||||||
# uses BmpImagePlugin.py to read the bitmap data.
|
|
||||||
#
|
|
||||||
# history:
|
|
||||||
# 96-05-27 fl Created
|
|
||||||
#
|
|
||||||
# Copyright (c) Secret Labs AB 1997.
|
|
||||||
# Copyright (c) Fredrik Lundh 1996.
|
|
||||||
#
|
|
||||||
# See the README file for information on usage and redistribution.
|
|
||||||
#
|
|
||||||
from . import BmpImagePlugin, Image
|
|
||||||
from ._binary import i16le as i16
|
|
||||||
from ._binary import i32le as i32
|
|
||||||
|
|
||||||
#
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
|
||||||
return prefix[:4] == b"\0\0\2\0"
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Image plugin for Windows Cursor files.
|
|
||||||
|
|
||||||
|
|
||||||
class CurImageFile(BmpImagePlugin.BmpImageFile):
|
|
||||||
format = "CUR"
|
|
||||||
format_description = "Windows Cursor"
|
|
||||||
|
|
||||||
def _open(self):
|
|
||||||
offset = self.fp.tell()
|
|
||||||
|
|
||||||
# check magic
|
|
||||||
s = self.fp.read(6)
|
|
||||||
if not _accept(s):
|
|
||||||
msg = "not a CUR file"
|
|
||||||
raise SyntaxError(msg)
|
|
||||||
|
|
||||||
# pick the largest cursor in the file
|
|
||||||
m = b""
|
|
||||||
for i in range(i16(s, 4)):
|
|
||||||
s = self.fp.read(16)
|
|
||||||
if not m:
|
|
||||||
m = s
|
|
||||||
elif s[0] > m[0] and s[1] > m[1]:
|
|
||||||
m = s
|
|
||||||
if not m:
|
|
||||||
msg = "No cursors were found"
|
|
||||||
raise TypeError(msg)
|
|
||||||
|
|
||||||
# load as bitmap
|
|
||||||
self._bitmap(i32(m, 12) + offset)
|
|
||||||
|
|
||||||
# patch up the bitmap height
|
|
||||||
self._size = self.size[0], self.size[1] // 2
|
|
||||||
d, e, o, a = self.tile[0]
|
|
||||||
self.tile[0] = d, (0, 0) + self.size, o, a
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
|
|
||||||
Image.register_open(CurImageFile.format, CurImageFile, _accept)
|
|
||||||
|
|
||||||
Image.register_extension(CurImageFile.format, ".cur")
|
|
||||||