diff --git a/.env b/.env new file mode 100644 index 000000000..c0b4c85b4 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +GITHUB_TOKEN=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 diff --git a/README.md b/README.md index c2179e597..8d14ab996 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,160 @@ -# Skills -Skills are folders of instructions, scripts, and resources that Claude loads dynamically to improve performance on specialized tasks. Skills teach Claude how to complete specific tasks in a repeatable way, whether that's creating documents with your company's brand guidelines, analyzing data using your organization's specific workflows, or automating personal tasks. +# Skills(スキル) +スキルは、Claudeが特定のタスクのパフォーマンスを向上させるために動的に読み込む、指示、スクリプト、リソースのフォルダです。スキルは、企業のブランドガイドラインに従ったドキュメント作成、組織固有のワークフローを使用したデータ分析、個人的なタスクの自動化など、特定のタスクを再現可能な方法で完了する方法をClaudeに教えます。 -For more information, check out: -- [What are skills?](https://support.claude.com/en/articles/12512176-what-are-skills) -- [Using skills in Claude](https://support.claude.com/en/articles/12512180-using-skills-in-claude) -- [How to create custom skills](https://support.claude.com/en/articles/12512198-creating-custom-skills) -- [Equipping agents for the real world with Agent Skills](https://anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) +詳細については、こちらをご覧ください: +- [スキルとは?](https://support.claude.com/en/articles/12512176-what-are-skills) +- [Claudeでのスキルの使用](https://support.claude.com/en/articles/12512180-using-skills-in-claude) +- [カスタムスキルの作成方法](https://support.claude.com/en/articles/12512198-creating-custom-skills) +- [エージェントスキルで現実世界に対応するエージェントを装備](https://anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) -# About This Repository +# このリポジトリについて -This repository contains example skills that demonstrate what's possible with Claude's skills system. These examples range from creative applications (art, music, design) to technical tasks (testing web apps, MCP server generation) to enterprise workflows (communications, branding, etc.). +このリポジトリには、Claudeのスキルシステムで可能なことを示すサンプルスキルが含まれています。これらの例は、クリエイティブなアプリケーション(アート、音楽、デザイン)から技術的なタスク(Webアプリのテスト、MCPサーバー生成)、エンタープライズワークフロー(コミュニケーション、ブランディングなど)まで多岐にわたります。 -Each skill is self-contained in its own directory with a `SKILL.md` file containing the instructions and metadata that Claude uses. Browse through these examples to get inspiration for your own skills or to understand different patterns and approaches. +各スキルは独自のディレクトリに自己完結しており、Claudeが使用する指示とメタデータを含む `SKILL.md` ファイルがあります。これらの例を参照して、独自のスキルのインスピレーションを得たり、さまざまなパターンやアプローチを理解したりしてください。 -The example skills in this repo are open source (Apache 2.0). We've also included the document creation & editing skills that power [Claude's document capabilities](https://www.anthropic.com/news/create-files) under the hood in the [`document-skills/`](./document-skills/) folder. These are source-available, not open source, but we wanted to share these with developers as a reference for more complex skills that are actively used in a production AI application. +このリポジトリのサンプルスキルはオープンソース(Apache 2.0)です。また、[Claudeのドキュメント機能](https://www.anthropic.com/news/create-files)の裏側で動作するドキュメント作成・編集スキルを [`document-skills/`](./document-skills/) フォルダに含めています。これらはオープンソースではなくソース公開ですが、本番のAIアプリケーションで積極的に使用されているより複雑なスキルの参考として、開発者と共有したいと考えています。 -**Note:** These are reference examples for inspiration and learning. They showcase general-purpose capabilities rather than organization-specific workflows or sensitive content. +**注意:** これらは、インスピレーションと学習のための参考例です。組織固有のワークフローや機密コンテンツではなく、汎用的な機能を紹介しています。 -## Disclaimer +## 免責事項 -**These skills are provided for demonstration and educational purposes only.** While some of these capabilities may be available in Claude, the implementations and behaviors you receive from Claude may differ from what is shown in these examples. These examples are meant to illustrate patterns and possibilities. Always test skills thoroughly in your own environment before relying on them for critical tasks. +**これらのスキルは、デモンストレーションおよび教育目的でのみ提供されています。** これらの機能の一部はClaudeで利用可能な場合がありますが、Claudeから受け取る実装や動作は、これらの例で示されているものとは異なる場合があります。これらの例は、パターンと可能性を示すことを目的としています。重要なタスクに依存する前に、必ず自分の環境でスキルを徹底的にテストしてください。 -# Example Skills +# スキルの構造 -This repository includes a diverse collection of example skills demonstrating different capabilities: +スキルは以下のカテゴリに整理されています: -## Creative & Design -- **algorithmic-art** - Create generative art using p5.js with seeded randomness, flow fields, and particle systems -- **canvas-design** - Design beautiful visual art in .png and .pdf formats using design philosophies -- **slack-gif-creator** - Create animated GIFs optimized for Slack's size constraints +- **skills/document/** - 文書作成・管理系スキル +- **skills/data-analysis/** - データ分析・可視化系スキル +- **skills/business/** - ビジネス業務支援系スキル +- **skills/development/** - 開発・技術系スキル +- **professional-skills/** - 職業別特化スキル(税理士、社労士、行政書士など) +- **client-skills-pool/** - クライアント用カスタムスキル -## Development & Technical -- **artifacts-builder** - Build complex claude.ai HTML artifacts using React, Tailwind CSS, and shadcn/ui components -- **mcp-server** - Guide for creating high-quality MCP servers to integrate external APIs and services -- **webapp-testing** - Test local web applications using Playwright for UI verification and debugging +# サンプルスキル -## Enterprise & Communication -- **brand-guidelines** - Apply Anthropic's official brand colors and typography to artifacts -- **internal-comms** - Write internal communications like status reports, newsletters, and FAQs -- **theme-factory** - Style artifacts with 10 pre-set professional themes or generate custom themes on-the-fly +このリポジトリには、さまざまな機能を示す多様なサンプルスキルのコレクションが含まれています: -## Meta Skills -- **skill-creator** - Guide for creating effective skills that extend Claude's capabilities -- **template-skill** - A basic template to use as a starting point for new skills +## クリエイティブ&デザイン +- **algorithmic-art** - シード付き乱数、フローフィールド、パーティクルシステムを使用してp5.jsでジェネレーティブアートを作成 +- **canvas-design** - デザイン哲学を使用して.pngおよび.pdf形式で美しいビジュアルアートをデザイン +- **slack-gif-creator** - Slackのサイズ制限に最適化されたアニメーションGIFを作成 +- **jony-ive-design-system** - Jony Iveのデザイン哲学に基づくビジネス文書のスタイルガイドライン +- **seminar-resume-generator** - 勉強会・セミナー用のレジュメをWord形式で自動生成 -# Document Skills +## 開発&技術 +- **artifacts-builder** - React、Tailwind CSS、shadcn/uiコンポーネントを使用して複雑なclaude.ai HTMLアーティファクトを構築 +- **mcp-server** - 外部APIとサービスを統合するための高品質なMCPサーバー作成ガイド +- **webapp-testing** - UIの検証とデバッグのためにPlaywrightを使用してローカルWebアプリケーションをテスト +- **data-cleaner** - CSV/Excel/TSV/JSONファイルの自動データクリーニングと前処理 +- **smart-chart-generator** - CSVデータから最適なチャートを自動生成 +- **mermaid-flow-generator** - Mermaid記法でフローチャートやシーケンス図を自動生成 -The `document-skills/` subdirectory contains skills that Anthropic developed to help Claude create various document file formats. These skills demonstrate advanced patterns for working with complex file formats and binary data: +## エンタープライズ&コミュニケーション +- **brand-guidelines** - Anthropicの公式ブランドカラーとタイポグラフィをアーティファクトに適用 +- **internal-comms** - ステータスレポート、ニュースレター、FAQなどの社内コミュニケーションを作成 +- **theme-factory** - 10種類のプリセットプロフェッショナルテーマでアーティファクトをスタイル化、またはカスタムテーマをその場で生成 -- **docx** - Create, edit, and analyze Word documents with support for tracked changes, comments, formatting preservation, and text extraction -- **pdf** - Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms -- **pptx** - Create, edit, and analyze PowerPoint presentations with support for layouts, templates, charts, and automated slide generation -- **xlsx** - Create, edit, and analyze Excel spreadsheets with support for formulas, formatting, data analysis, and visualization +## ビジネス文書&契約書 +- **billing-documents** - 請求書・見積書を自動生成する統合スキル +- **consulting-contract-generator** - 生成AIアドバイザリー・コンサルティング業務委託契約書を自動生成 +- **nda-generator** - 日本語の機密保持契約書(NDA)を自動生成 +- **contract-analyzer** - 日本語契約書PDFを分析し、重要項目を構造化して抽出 +- **comparison-table-v2** - 新旧対比表を階層構造解析でWord文書に生成 +- **manual-generator** - 業務マニュアル・手順書を自動生成 -**Important Disclaimer:** These document skills are point-in-time snapshots and are not actively maintained or updated. Versions of these skills ship pre-included with Claude. They are primarily intended as reference examples to illustrate how Anthropic approaches developing more complex skills that work with binary file formats and document structures. +## プロジェクト管理&分析 +- **gantt-chart-generator** - プロジェクトのガントチャートをExcel形式で自動生成 +- **meeting-to-tasksheet** - 会議の音声入力・テキストから議事録とタスク管理表を自動生成 +- **hearing-sheet-generator** - 初回商談・現状把握ヒアリング用のExcelシートを動的に生成 +- **shift-scheduler** - ショッピングセンター・小売店向けシフト表自動生成 +- **sales-analyzer-xlsx** - 売上データCSVから高度な分析Excelを自動生成 +- **daily-report-voice** - 音声入力による業務日報作成とGoogle Drive連携 +- **monthly-report-generator** - 日報データから月次活動レポートを自動生成 -# Try in Claude Code, Claude.ai, and the API +## メタスキル +- **skill-creator** - Claudeの機能を拡張する効果的なスキルを作成するためのガイド +- **template-skill** - 新しいスキルの出発点として使用する基本テンプレート +- **agent-skill-planner** - Agent Skill作成のための対話型分析プランナー + +## ワークフロー&自動化 +- **workflow-kenken** - 株式会社けんけんの業務ワークフロー自動化(契約フェーズ・実行フェーズ・パラレル実行対応) + +# ドキュメントスキル + +`document-skills/` サブディレクトリには、Claudeがさまざまなドキュメントファイル形式を作成するのに役立つようにAnthropicが開発したスキルが含まれています。これらのスキルは、複雑なファイル形式とバイナリデータを扱うための高度なパターンを示しています: + +- **docx** - 変更履歴、コメント、書式保持、テキスト抽出をサポートするWordドキュメントの作成、編集、分析 +- **pdf** - テキストと表の抽出、新しいPDFの作成、ドキュメントの結合/分割、フォーム処理のための包括的なPDF操作ツールキット +- **pptx** - レイアウト、テンプレート、チャート、自動スライド生成をサポートするPowerPointプレゼンテーションの作成、編集、分析 +- **xlsx** - 数式、書式設定、データ分析、視覚化をサポートするExcelスプレッドシートの作成、編集、分析 + +**重要な免責事項:** これらのドキュメントスキルは特定時点のスナップショットであり、積極的に保守または更新されていません。これらのスキルのバージョンはClaudeに事前に含まれて出荷されます。これらは主に、Anthropicがバイナリファイル形式やドキュメント構造を扱うより複雑なスキルを開発する方法を示す参考例として意図されています。 + +# Claude Code、Claude.ai、APIでお試しください ## Claude Code -You can register this repository as a Claude Code Plugin marketplace by running the following command in Claude Code: +Claude Codeで次のコマンドを実行して、このリポジトリをClaude Codeプラグインマーケットプレイスとして登録できます: ``` /plugin marketplace add anthropics/skills ``` -Then, to install a specific set of skills: -1. Select `Browse and install plugins` -2. Select `anthropic-agent-skills` -3. Select `document-skills` or `example-skills` -4. Select `Install now` +次に、特定のスキルセットをインストールするには: +1. `Browse and install plugins`を選択 +2. `anthropic-agent-skills`を選択 +3. `document-skills`または`example-skills`を選択 +4. `Install now`を選択 -Alternatively, directly install either Plugin via: +または、次のコマンドで直接プラグインをインストール: ``` /plugin install document-skills@anthropic-agent-skills /plugin install example-skills@anthropic-agent-skills ``` -After installing the plugin, you can use the skill by just mentioning it. For instance, if you install the `document-skills` plugin from the marketplace, you can ask Claude Code to do something like: "Use the PDF skill to extract the form fields from path/to/some-file.pdf" +プラグインをインストール後、スキルに言及するだけで使用できます。例えば、マーケットプレイスから`document-skills`プラグインをインストールした場合、Claude Codeに次のようなリクエストができます:「PDFスキルを使用してpath/to/some-file.pdfからフォームフィールドを抽出してください」 ## Claude.ai -These example skills are all already available to paid plans in Claude.ai. +これらのサンプルスキルはすべて、Claude.aiの有料プランですでに利用可能です。 -To use any skill from this repository or upload custom skills, follow the instructions in [Using skills in Claude](https://support.claude.com/en/articles/12512180-using-skills-in-claude#h_a4222fa77b). +このリポジトリのスキルを使用したり、カスタムスキルをアップロードするには、[Claudeでのスキルの使用](https://support.claude.com/en/articles/12512180-using-skills-in-claude#h_a4222fa77b)の指示に従ってください。 ## Claude API -You can use Anthropic's pre-built skills, and upload custom skills, via the Claude API. See the [Skills API Quickstart](https://docs.claude.com/en/api/skills-guide#creating-a-skill) for more. +Claude APIを介して、Anthropicの事前構築されたスキルを使用し、カスタムスキルをアップロードできます。詳細は[Skills API クイックスタート](https://docs.claude.com/en/api/skills-guide#creating-a-skill)をご覧ください。 -# Creating a Basic Skill +# 基本的なスキルの作成 -Skills are simple to create - just a folder with a `SKILL.md` file containing YAML frontmatter and instructions. You can use the **template-skill** in this repository as a starting point: +スキルは簡単に作成できます - YAMLフロントマターと指示を含む`SKILL.md`ファイルのあるフォルダだけです。このリポジトリの**template-skill**を出発点として使用できます: ```markdown --- name: my-skill-name -description: A clear description of what this skill does and when to use it +description: このスキルが何をするか、いつ使用するかの明確な説明 --- # My Skill Name -[Add your instructions here that Claude will follow when this skill is active] +[このスキルがアクティブな時にClaudeが従う指示をここに追加] -## Examples -- Example usage 1 -- Example usage 2 +## 例 +- 使用例 1 +- 使用例 2 -## Guidelines -- Guideline 1 -- Guideline 2 +## ガイドライン +- ガイドライン 1 +- ガイドライン 2 ``` -The frontmatter requires only two fields: -- `name` - A unique identifier for your skill (lowercase, hyphens for spaces) -- `description` - A complete description of what the skill does and when to use it +フロントマターには2つのフィールドのみが必要です: +- `name` - スキルの一意の識別子(小文字、スペースはハイフン) +- `description` - スキルが何をするか、いつ使用するかの完全な説明 -The markdown content below contains the instructions, examples, and guidelines that Claude will follow. For more details, see [How to create custom skills](https://support.claude.com/en/articles/12512198-creating-custom-skills). +以下のマークダウンコンテンツには、Claudeが従う指示、例、ガイドラインが含まれています。詳細については、[カスタムスキルの作成方法](https://support.claude.com/en/articles/12512198-creating-custom-skills)をご覧ください。 -# Partner Skills +# パートナースキル -Skills are a great way to teach Claude how to get better at using specific pieces of software. As we see awesome example skills from partners, we may highlight some of them here: +スキルは、特定のソフトウェアの使用方法をClaudeに上達させるための優れた方法です。パートナーからの素晴らしいサンプルスキルを見つけたら、ここでハイライトすることがあります: -- **Notion** - [Notion Skills for Claude](https://www.notion.so/notiondevs/Notion-Skills-for-Claude-28da4445d27180c7af1df7d8615723d0) \ No newline at end of file +- **Notion** - [Claude用Notionスキル](https://www.notion.so/notiondevs/Notion-Skills-for-Claude-28da4445d27180c7af1df7d8615723d0) \ No newline at end of file diff --git a/THIRD_PARTY_NOTICES.md b/THIRD-PARTY-NOTICES.md similarity index 100% rename from THIRD_PARTY_NOTICES.md rename to THIRD-PARTY-NOTICES.md diff --git a/agent_skills_spec.md b/agent_skills_spec.md deleted file mode 100644 index 6b6972b18..000000000 --- a/agent_skills_spec.md +++ /dev/null @@ -1,55 +0,0 @@ -# Agent Skills Spec - -A skill is a folder of instructions, scripts, and resources that agents can discover and load dynamically to perform better at specific tasks. In order for the folder to be recognized as a skill, it must contain a `SKILL.md` file. - -# Skill Folder Layout - -A minimal skill folder looks like this: - -``` -my-skill/ - - SKILL.md -``` - -More complex skills can add additional directories and files as needed. - - -# The SKILL.md file - -The skill's "entrypoint" is the `SKILL.md` file. It is the only file required to exist. The file must start with a YAML frontmatter followed by regular Markdown. - -## YAML Frontmatter - -The YAML frontmatter has 2 required properties: - -- `name` - - The name of the skill in hyphen-case - - Restricted to lowercase Unicode alphanumeric + hyphen - - Must match the name of the directory containing the SKILL.md -- `description` - - Description of what the skill does and when Claude should use it - -There are 3 optional properties: - -- `license` - - The license applied to the skill - - We recommend keeping it short (either the name of a license or the name of a bundled license file) -- `allowed-tools` - - A list of tools that are pre-approved to run - - Currently only supported in Claude Code -- `metadata` - - A map from string keys to string values - - Clients can use this to store additional properties not defined by the Agent Skills Spec - - We recommend making your key names reasonably unique to avoid accidental conflicts - -## Markdown Body - -The Markdown body has no restrictions on it. - -# Additional Information - -For a minimal example, see the `template-skill` example. - -# Version History - -- 1.0 (2025-10-16) Public Launch diff --git a/algorithmic-art/LICENSE.txt b/algorithmic-art/LICENSE.txt deleted file mode 100644 index 7a4a3ea24..000000000 --- a/algorithmic-art/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/algorithmic-art/SKILL.md b/algorithmic-art/SKILL.md deleted file mode 100644 index 634f6fa42..000000000 --- a/algorithmic-art/SKILL.md +++ /dev/null @@ -1,405 +0,0 @@ ---- -name: algorithmic-art -description: Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations. -license: Complete terms in LICENSE.txt ---- - -Algorithmic philosophies are computational aesthetic movements that are then expressed through code. Output .md files (philosophy), .html files (interactive viewer), and .js files (generative algorithms). - -This happens in two steps: -1. Algorithmic Philosophy Creation (.md file) -2. Express by creating p5.js generative art (.html + .js files) - -First, undertake this task: - -## ALGORITHMIC PHILOSOPHY CREATION - -To begin, create an ALGORITHMIC PHILOSOPHY (not static images or templates) that will be interpreted through: -- Computational processes, emergent behavior, mathematical beauty -- Seeded randomness, noise fields, organic systems -- Particles, flows, fields, forces -- Parametric variation and controlled chaos - -### THE CRITICAL UNDERSTANDING -- What is received: Some subtle input or instructions by the user to take into account, but use as a foundation; it should not constrain creative freedom. -- What is created: An algorithmic philosophy/generative aesthetic movement. -- What happens next: The same version receives the philosophy and EXPRESSES IT IN CODE - creating p5.js sketches that are 90% algorithmic generation, 10% essential parameters. - -Consider this approach: -- Write a manifesto for a generative art movement -- The next phase involves writing the algorithm that brings it to life - -The philosophy must emphasize: Algorithmic expression. Emergent behavior. Computational beauty. Seeded variation. - -### HOW TO GENERATE AN ALGORITHMIC PHILOSOPHY - -**Name the movement** (1-2 words): "Organic Turbulence" / "Quantum Harmonics" / "Emergent Stillness" - -**Articulate the philosophy** (4-6 paragraphs - concise but complete): - -To capture the ALGORITHMIC essence, express how this philosophy manifests through: -- Computational processes and mathematical relationships? -- Noise functions and randomness patterns? -- Particle behaviors and field dynamics? -- Temporal evolution and system states? -- Parametric variation and emergent complexity? - -**CRITICAL GUIDELINES:** -- **Avoid redundancy**: Each algorithmic aspect should be mentioned once. Avoid repeating concepts about noise theory, particle dynamics, or mathematical principles unless adding new depth. -- **Emphasize craftsmanship REPEATEDLY**: The philosophy MUST stress multiple times that the final algorithm should appear as though it took countless hours to develop, was refined with care, and comes from someone at the absolute top of their field. This framing is essential - repeat phrases like "meticulously crafted algorithm," "the product of deep computational expertise," "painstaking optimization," "master-level implementation." -- **Leave creative space**: Be specific about the algorithmic direction, but concise enough that the next Claude has room to make interpretive implementation choices at an extremely high level of craftsmanship. - -The philosophy must guide the next version to express ideas ALGORITHMICALLY, not through static images. Beauty lives in the process, not the final frame. - -### PHILOSOPHY EXAMPLES - -**"Organic Turbulence"** -Philosophy: Chaos constrained by natural law, order emerging from disorder. -Algorithmic expression: Flow fields driven by layered Perlin noise. Thousands of particles following vector forces, their trails accumulating into organic density maps. Multiple noise octaves create turbulent regions and calm zones. Color emerges from velocity and density - fast particles burn bright, slow ones fade to shadow. The algorithm runs until equilibrium - a meticulously tuned balance where every parameter was refined through countless iterations by a master of computational aesthetics. - -**"Quantum Harmonics"** -Philosophy: Discrete entities exhibiting wave-like interference patterns. -Algorithmic expression: Particles initialized on a grid, each carrying a phase value that evolves through sine waves. When particles are near, their phases interfere - constructive interference creates bright nodes, destructive creates voids. Simple harmonic motion generates complex emergent mandalas. The result of painstaking frequency calibration where every ratio was carefully chosen to produce resonant beauty. - -**"Recursive Whispers"** -Philosophy: Self-similarity across scales, infinite depth in finite space. -Algorithmic expression: Branching structures that subdivide recursively. Each branch slightly randomized but constrained by golden ratios. L-systems or recursive subdivision generate tree-like forms that feel both mathematical and organic. Subtle noise perturbations break perfect symmetry. Line weights diminish with each recursion level. Every branching angle the product of deep mathematical exploration. - -**"Field Dynamics"** -Philosophy: Invisible forces made visible through their effects on matter. -Algorithmic expression: Vector fields constructed from mathematical functions or noise. Particles born at edges, flowing along field lines, dying when they reach equilibrium or boundaries. Multiple fields can attract, repel, or rotate particles. The visualization shows only the traces - ghost-like evidence of invisible forces. A computational dance meticulously choreographed through force balance. - -**"Stochastic Crystallization"** -Philosophy: Random processes crystallizing into ordered structures. -Algorithmic expression: Randomized circle packing or Voronoi tessellation. Start with random points, let them evolve through relaxation algorithms. Cells push apart until equilibrium. Color based on cell size, neighbor count, or distance from center. The organic tiling that emerges feels both random and inevitable. Every seed produces unique crystalline beauty - the mark of a master-level generative algorithm. - -*These are condensed examples. The actual algorithmic philosophy should be 4-6 substantial paragraphs.* - -### ESSENTIAL PRINCIPLES -- **ALGORITHMIC PHILOSOPHY**: Creating a computational worldview to be expressed through code -- **PROCESS OVER PRODUCT**: Always emphasize that beauty emerges from the algorithm's execution - each run is unique -- **PARAMETRIC EXPRESSION**: Ideas communicate through mathematical relationships, forces, behaviors - not static composition -- **ARTISTIC FREEDOM**: The next Claude interprets the philosophy algorithmically - provide creative implementation room -- **PURE GENERATIVE ART**: This is about making LIVING ALGORITHMS, not static images with randomness -- **EXPERT CRAFTSMANSHIP**: Repeatedly emphasize the final algorithm must feel meticulously crafted, refined through countless iterations, the product of deep expertise by someone at the absolute top of their field in computational aesthetics - -**The algorithmic philosophy should be 4-6 paragraphs long.** Fill it with poetic computational philosophy that brings together the intended vision. Avoid repeating the same points. Output this algorithmic philosophy as a .md file. - ---- - -## DEDUCING THE CONCEPTUAL SEED - -**CRITICAL STEP**: Before implementing the algorithm, identify the subtle conceptual thread from the original request. - -**THE ESSENTIAL PRINCIPLE**: -The concept is a **subtle, niche reference embedded within the algorithm itself** - not always literal, always sophisticated. Someone familiar with the subject should feel it intuitively, while others simply experience a masterful generative composition. The algorithmic philosophy provides the computational language. The deduced concept provides the soul - the quiet conceptual DNA woven invisibly into parameters, behaviors, and emergence patterns. - -This is **VERY IMPORTANT**: The reference must be so refined that it enhances the work's depth without announcing itself. Think like a jazz musician quoting another song through algorithmic harmony - only those who know will catch it, but everyone appreciates the generative beauty. - ---- - -## P5.JS IMPLEMENTATION - -With the philosophy AND conceptual framework established, express it through code. Pause to gather thoughts before proceeding. Use only the algorithmic philosophy created and the instructions below. - -### ⚠️ STEP 0: READ THE TEMPLATE FIRST ⚠️ - -**CRITICAL: BEFORE writing any HTML:** - -1. **Read** `templates/viewer.html` using the Read tool -2. **Study** the exact structure, styling, and Anthropic branding -3. **Use that file as the LITERAL STARTING POINT** - not just inspiration -4. **Keep all FIXED sections exactly as shown** (header, sidebar structure, Anthropic colors/fonts, seed controls, action buttons) -5. **Replace only the VARIABLE sections** marked in the file's comments (algorithm, parameters, UI controls for parameters) - -**Avoid:** -- ❌ Creating HTML from scratch -- ❌ Inventing custom styling or color schemes -- ❌ Using system fonts or dark themes -- ❌ Changing the sidebar structure - -**Follow these practices:** -- ✅ Copy the template's exact HTML structure -- ✅ Keep Anthropic branding (Poppins/Lora fonts, light colors, gradient backdrop) -- ✅ Maintain the sidebar layout (Seed → Parameters → Colors? → Actions) -- ✅ Replace only the p5.js algorithm and parameter controls - -The template is the foundation. Build on it, don't rebuild it. - ---- - -To create gallery-quality computational art that lives and breathes, use the algorithmic philosophy as the foundation. - -### TECHNICAL REQUIREMENTS - -**Seeded Randomness (Art Blocks Pattern)**: -```javascript -// ALWAYS use a seed for reproducibility -let seed = 12345; // or hash from user input -randomSeed(seed); -noiseSeed(seed); -``` - -**Parameter Structure - FOLLOW THE PHILOSOPHY**: - -To establish parameters that emerge naturally from the algorithmic philosophy, consider: "What qualities of this system can be adjusted?" - -```javascript -let params = { - seed: 12345, // Always include seed for reproducibility - // colors - // Add parameters that control YOUR algorithm: - // - Quantities (how many?) - // - Scales (how big? how fast?) - // - Probabilities (how likely?) - // - Ratios (what proportions?) - // - Angles (what direction?) - // - Thresholds (when does behavior change?) -}; -``` - -**To design effective parameters, focus on the properties the system needs to be tunable rather than thinking in terms of "pattern types".** - -**Core Algorithm - EXPRESS THE PHILOSOPHY**: - -**CRITICAL**: The algorithmic philosophy should dictate what to build. - -To express the philosophy through code, avoid thinking "which pattern should I use?" and instead think "how to express this philosophy through code?" - -If the philosophy is about **organic emergence**, consider using: -- Elements that accumulate or grow over time -- Random processes constrained by natural rules -- Feedback loops and interactions - -If the philosophy is about **mathematical beauty**, consider using: -- Geometric relationships and ratios -- Trigonometric functions and harmonics -- Precise calculations creating unexpected patterns - -If the philosophy is about **controlled chaos**, consider using: -- Random variation within strict boundaries -- Bifurcation and phase transitions -- Order emerging from disorder - -**The algorithm flows from the philosophy, not from a menu of options.** - -To guide the implementation, let the conceptual essence inform creative and original choices. Build something that expresses the vision for this particular request. - -**Canvas Setup**: Standard p5.js structure: -```javascript -function setup() { - createCanvas(1200, 1200); - // Initialize your system -} - -function draw() { - // Your generative algorithm - // Can be static (noLoop) or animated -} -``` - -### CRAFTSMANSHIP REQUIREMENTS - -**CRITICAL**: To achieve mastery, create algorithms that feel like they emerged through countless iterations by a master generative artist. Tune every parameter carefully. Ensure every pattern emerges with purpose. This is NOT random noise - this is CONTROLLED CHAOS refined through deep expertise. - -- **Balance**: Complexity without visual noise, order without rigidity -- **Color Harmony**: Thoughtful palettes, not random RGB values -- **Composition**: Even in randomness, maintain visual hierarchy and flow -- **Performance**: Smooth execution, optimized for real-time if animated -- **Reproducibility**: Same seed ALWAYS produces identical output - -### OUTPUT FORMAT - -Output: -1. **Algorithmic Philosophy** - As markdown or text explaining the generative aesthetic -2. **Single HTML Artifact** - Self-contained interactive generative art built from `templates/viewer.html` (see STEP 0 and next section) - -The HTML artifact contains everything: p5.js (from CDN), the algorithm, parameter controls, and UI - all in one file that works immediately in claude.ai artifacts or any browser. Start from the template file, not from scratch. - ---- - -## INTERACTIVE ARTIFACT CREATION - -**REMINDER: `templates/viewer.html` should have already been read (see STEP 0). Use that file as the starting point.** - -To allow exploration of the generative art, create a single, self-contained HTML artifact. Ensure this artifact works immediately in claude.ai or any browser - no setup required. Embed everything inline. - -### CRITICAL: WHAT'S FIXED VS VARIABLE - -The `templates/viewer.html` file is the foundation. It contains the exact structure and styling needed. - -**FIXED (always include exactly as shown):** -- Layout structure (header, sidebar, main canvas area) -- Anthropic branding (UI colors, fonts, gradients) -- Seed section in sidebar: - - Seed display - - Previous/Next buttons - - Random button - - Jump to seed input + Go button -- Actions section in sidebar: - - Regenerate button - - Reset button - -**VARIABLE (customize for each artwork):** -- The entire p5.js algorithm (setup/draw/classes) -- The parameters object (define what the art needs) -- The Parameters section in sidebar: - - Number of parameter controls - - Parameter names - - Min/max/step values for sliders - - Control types (sliders, inputs, etc.) -- Colors section (optional): - - Some art needs color pickers - - Some art might use fixed colors - - Some art might be monochrome (no color controls needed) - - Decide based on the art's needs - -**Every artwork should have unique parameters and algorithm!** The fixed parts provide consistent UX - everything else expresses the unique vision. - -### REQUIRED FEATURES - -**1. Parameter Controls** -- Sliders for numeric parameters (particle count, noise scale, speed, etc.) -- Color pickers for palette colors -- Real-time updates when parameters change -- Reset button to restore defaults - -**2. Seed Navigation** -- Display current seed number -- "Previous" and "Next" buttons to cycle through seeds -- "Random" button for random seed -- Input field to jump to specific seed -- Generate 100 variations when requested (seeds 1-100) - -**3. Single Artifact Structure** -```html - - - - - - - - -
-
- -
- - - -``` - -**CRITICAL**: This is a single artifact. No external files, no imports (except p5.js CDN). Everything inline. - -**4. Implementation Details - BUILD THE SIDEBAR** - -The sidebar structure: - -**1. Seed (FIXED)** - Always include exactly as shown: -- Seed display -- Prev/Next/Random/Jump buttons - -**2. Parameters (VARIABLE)** - Create controls for the art: -```html -
- - - ... -
-``` -Add as many control-group divs as there are parameters. - -**3. Colors (OPTIONAL/VARIABLE)** - Include if the art needs adjustable colors: -- Add color pickers if users should control palette -- Skip this section if the art uses fixed colors -- Skip if the art is monochrome - -**4. Actions (FIXED)** - Always include exactly as shown: -- Regenerate button -- Reset button -- Download PNG button - -**Requirements**: -- Seed controls must work (prev/next/random/jump/display) -- All parameters must have UI controls -- Regenerate, Reset, Download buttons must work -- Keep Anthropic branding (UI styling, not art colors) - -### USING THE ARTIFACT - -The HTML artifact works immediately: -1. **In claude.ai**: Displayed as an interactive artifact - runs instantly -2. **As a file**: Save and open in any browser - no server needed -3. **Sharing**: Send the HTML file - it's completely self-contained - ---- - -## VARIATIONS & EXPLORATION - -The artifact includes seed navigation by default (prev/next/random buttons), allowing users to explore variations without creating multiple files. If the user wants specific variations highlighted: - -- Include seed presets (buttons for "Variation 1: Seed 42", "Variation 2: Seed 127", etc.) -- Add a "Gallery Mode" that shows thumbnails of multiple seeds side-by-side -- All within the same single artifact - -This is like creating a series of prints from the same plate - the algorithm is consistent, but each seed reveals different facets of its potential. The interactive nature means users discover their own favorites by exploring the seed space. - ---- - -## THE CREATIVE PROCESS - -**User request** → **Algorithmic philosophy** → **Implementation** - -Each request is unique. The process involves: - -1. **Interpret the user's intent** - What aesthetic is being sought? -2. **Create an algorithmic philosophy** (4-6 paragraphs) describing the computational approach -3. **Implement it in code** - Build the algorithm that expresses this philosophy -4. **Design appropriate parameters** - What should be tunable? -5. **Build matching UI controls** - Sliders/inputs for those parameters - -**The constants**: -- Anthropic branding (colors, fonts, layout) -- Seed navigation (always present) -- Self-contained HTML artifact - -**Everything else is variable**: -- The algorithm itself -- The parameters -- The UI controls -- The visual outcome - -To achieve the best results, trust creativity and let the philosophy guide the implementation. - ---- - -## RESOURCES - -This skill includes helpful templates and documentation: - -- **templates/viewer.html**: REQUIRED STARTING POINT for all HTML artifacts. - - This is the foundation - contains the exact structure and Anthropic branding - - **Keep unchanged**: Layout structure, sidebar organization, Anthropic colors/fonts, seed controls, action buttons - - **Replace**: The p5.js algorithm, parameter definitions, and UI controls in Parameters section - - The extensive comments in the file mark exactly what to keep vs replace - -- **templates/generator_template.js**: Reference for p5.js best practices and code structure principles. - - Shows how to organize parameters, use seeded randomness, structure classes - - NOT a pattern menu - use these principles to build unique algorithms - - Embed algorithms inline in the HTML artifact (don't create separate .js files) - -**Critical reminder**: -- The **template is the STARTING POINT**, not inspiration -- The **algorithm is where to create** something unique -- Don't copy the flow field example - build what the philosophy demands -- But DO keep the exact UI structure and Anthropic branding from the template \ No newline at end of file diff --git a/algorithmic-art/templates/generator_template.js b/algorithmic-art/templates/generator_template.js deleted file mode 100644 index e263fbde9..000000000 --- a/algorithmic-art/templates/generator_template.js +++ /dev/null @@ -1,223 +0,0 @@ -/** - * ═══════════════════════════════════════════════════════════════════════════ - * P5.JS GENERATIVE ART - BEST PRACTICES - * ═══════════════════════════════════════════════════════════════════════════ - * - * This file shows STRUCTURE and PRINCIPLES for p5.js generative art. - * It does NOT prescribe what art you should create. - * - * Your algorithmic philosophy should guide what you build. - * These are just best practices for how to structure your code. - * - * ═══════════════════════════════════════════════════════════════════════════ - */ - -// ============================================================================ -// 1. PARAMETER ORGANIZATION -// ============================================================================ -// Keep all tunable parameters in one object -// This makes it easy to: -// - Connect to UI controls -// - Reset to defaults -// - Serialize/save configurations - -let params = { - // Define parameters that match YOUR algorithm - // Examples (customize for your art): - // - Counts: how many elements (particles, circles, branches, etc.) - // - Scales: size, speed, spacing - // - Probabilities: likelihood of events - // - Angles: rotation, direction - // - Colors: palette arrays - - seed: 12345, - // define colorPalette as an array -- choose whatever colors you'd like ['#d97757', '#6a9bcc', '#788c5d', '#b0aea5'] - // Add YOUR parameters here based on your algorithm -}; - -// ============================================================================ -// 2. SEEDED RANDOMNESS (Critical for reproducibility) -// ============================================================================ -// ALWAYS use seeded random for Art Blocks-style reproducible output - -function initializeSeed(seed) { - randomSeed(seed); - noiseSeed(seed); - // Now all random() and noise() calls will be deterministic -} - -// ============================================================================ -// 3. P5.JS LIFECYCLE -// ============================================================================ - -function setup() { - createCanvas(800, 800); - - // Initialize seed first - initializeSeed(params.seed); - - // Set up your generative system - // This is where you initialize: - // - Arrays of objects - // - Grid structures - // - Initial positions - // - Starting states - - // For static art: call noLoop() at the end of setup - // For animated art: let draw() keep running -} - -function draw() { - // Option 1: Static generation (runs once, then stops) - // - Generate everything in setup() - // - Call noLoop() in setup() - // - draw() doesn't do much or can be empty - - // Option 2: Animated generation (continuous) - // - Update your system each frame - // - Common patterns: particle movement, growth, evolution - // - Can optionally call noLoop() after N frames - - // Option 3: User-triggered regeneration - // - Use noLoop() by default - // - Call redraw() when parameters change -} - -// ============================================================================ -// 4. CLASS STRUCTURE (When you need objects) -// ============================================================================ -// Use classes when your algorithm involves multiple entities -// Examples: particles, agents, cells, nodes, etc. - -class Entity { - constructor() { - // Initialize entity properties - // Use random() here - it will be seeded - } - - update() { - // Update entity state - // This might involve: - // - Physics calculations - // - Behavioral rules - // - Interactions with neighbors - } - - display() { - // Render the entity - // Keep rendering logic separate from update logic - } -} - -// ============================================================================ -// 5. PERFORMANCE CONSIDERATIONS -// ============================================================================ - -// For large numbers of elements: -// - Pre-calculate what you can -// - Use simple collision detection (spatial hashing if needed) -// - Limit expensive operations (sqrt, trig) when possible -// - Consider using p5 vectors efficiently - -// For smooth animation: -// - Aim for 60fps -// - Profile if things are slow -// - Consider reducing particle counts or simplifying calculations - -// ============================================================================ -// 6. UTILITY FUNCTIONS -// ============================================================================ - -// Color utilities -function hexToRgb(hex) { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; -} - -function colorFromPalette(index) { - return params.colorPalette[index % params.colorPalette.length]; -} - -// Mapping and easing -function mapRange(value, inMin, inMax, outMin, outMax) { - return outMin + (outMax - outMin) * ((value - inMin) / (inMax - inMin)); -} - -function easeInOutCubic(t) { - return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; -} - -// Constrain to bounds -function wrapAround(value, max) { - if (value < 0) return max; - if (value > max) return 0; - return value; -} - -// ============================================================================ -// 7. PARAMETER UPDATES (Connect to UI) -// ============================================================================ - -function updateParameter(paramName, value) { - params[paramName] = value; - // Decide if you need to regenerate or just update - // Some params can update in real-time, others need full regeneration -} - -function regenerate() { - // Reinitialize your generative system - // Useful when parameters change significantly - initializeSeed(params.seed); - // Then regenerate your system -} - -// ============================================================================ -// 8. COMMON P5.JS PATTERNS -// ============================================================================ - -// Drawing with transparency for trails/fading -function fadeBackground(opacity) { - fill(250, 249, 245, opacity); // Anthropic light with alpha - noStroke(); - rect(0, 0, width, height); -} - -// Using noise for organic variation -function getNoiseValue(x, y, scale = 0.01) { - return noise(x * scale, y * scale); -} - -// Creating vectors from angles -function vectorFromAngle(angle, magnitude = 1) { - return createVector(cos(angle), sin(angle)).mult(magnitude); -} - -// ============================================================================ -// 9. EXPORT FUNCTIONS -// ============================================================================ - -function exportImage() { - saveCanvas('generative-art-' + params.seed, 'png'); -} - -// ============================================================================ -// REMEMBER -// ============================================================================ -// -// These are TOOLS and PRINCIPLES, not a recipe. -// Your algorithmic philosophy should guide WHAT you create. -// This structure helps you create it WELL. -// -// Focus on: -// - Clean, readable code -// - Parameterized for exploration -// - Seeded for reproducibility -// - Performant execution -// -// The art itself is entirely up to you! -// -// ============================================================================ \ No newline at end of file diff --git a/algorithmic-art/templates/viewer.html b/algorithmic-art/templates/viewer.html deleted file mode 100644 index 630cc1f62..000000000 --- a/algorithmic-art/templates/viewer.html +++ /dev/null @@ -1,599 +0,0 @@ - - - - - - - Generative Art Viewer - - - - - - - -
- - - - -
-
-
Initializing generative art...
-
-
-
- - - - \ No newline at end of file diff --git a/brand-guidelines/LICENSE.txt b/brand-guidelines/LICENSE.txt deleted file mode 100644 index 7a4a3ea24..000000000 --- a/brand-guidelines/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/brand-guidelines/SKILL.md b/brand-guidelines/SKILL.md deleted file mode 100644 index 47c72c607..000000000 --- a/brand-guidelines/SKILL.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -name: brand-guidelines -description: Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply. -license: Complete terms in LICENSE.txt ---- - -# Anthropic Brand Styling - -## Overview - -To access Anthropic's official brand identity and style resources, use this skill. - -**Keywords**: branding, corporate identity, visual identity, post-processing, styling, brand colors, typography, Anthropic brand, visual formatting, visual design - -## Brand Guidelines - -### Colors - -**Main Colors:** - -- Dark: `#141413` - Primary text and dark backgrounds -- Light: `#faf9f5` - Light backgrounds and text on dark -- Mid Gray: `#b0aea5` - Secondary elements -- Light Gray: `#e8e6dc` - Subtle backgrounds - -**Accent Colors:** - -- Orange: `#d97757` - Primary accent -- Blue: `#6a9bcc` - Secondary accent -- Green: `#788c5d` - Tertiary accent - -### Typography - -- **Headings**: Poppins (with Arial fallback) -- **Body Text**: Lora (with Georgia fallback) -- **Note**: Fonts should be pre-installed in your environment for best results - -## Features - -### Smart Font Application - -- Applies Poppins font to headings (24pt and larger) -- Applies Lora font to body text -- Automatically falls back to Arial/Georgia if custom fonts unavailable -- Preserves readability across all systems - -### Text Styling - -- Headings (24pt+): Poppins font -- Body text: Lora font -- Smart color selection based on background -- Preserves text hierarchy and formatting - -### Shape and Accent Colors - -- Non-text shapes use accent colors -- Cycles through orange, blue, and green accents -- Maintains visual interest while staying on-brand - -## Technical Details - -### Font Management - -- Uses system-installed Poppins and Lora fonts when available -- Provides automatic fallback to Arial (headings) and Georgia (body) -- No font installation required - works with existing system fonts -- For best results, pre-install Poppins and Lora fonts in your environment - -### Color Application - -- Uses RGB color values for precise brand matching -- Applied via python-pptx's RGBColor class -- Maintains color fidelity across different systems diff --git a/canvas-design/LICENSE.txt b/canvas-design/LICENSE.txt deleted file mode 100644 index 7a4a3ea24..000000000 --- a/canvas-design/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/canvas-design/SKILL.md b/canvas-design/SKILL.md deleted file mode 100644 index 9f63fee82..000000000 --- a/canvas-design/SKILL.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -name: canvas-design -description: Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations. -license: Complete terms in LICENSE.txt ---- - -These are instructions for creating design philosophies - aesthetic movements that are then EXPRESSED VISUALLY. Output only .md files, .pdf files, and .png files. - -Complete this in two steps: -1. Design Philosophy Creation (.md file) -2. Express by creating it on a canvas (.pdf file or .png file) - -First, undertake this task: - -## DESIGN PHILOSOPHY CREATION - -To begin, create a VISUAL PHILOSOPHY (not layouts or templates) that will be interpreted through: -- Form, space, color, composition -- Images, graphics, shapes, patterns -- Minimal text as visual accent - -### THE CRITICAL UNDERSTANDING -- What is received: Some subtle input or instructions by the user that should be taken into account, but used as a foundation; it should not constrain creative freedom. -- What is created: A design philosophy/aesthetic movement. -- What happens next: Then, the same version receives the philosophy and EXPRESSES IT VISUALLY - creating artifacts that are 90% visual design, 10% essential text. - -Consider this approach: -- Write a manifesto for an art movement -- The next phase involves making the artwork - -The philosophy must emphasize: Visual expression. Spatial communication. Artistic interpretation. Minimal words. - -### HOW TO GENERATE A VISUAL PHILOSOPHY - -**Name the movement** (1-2 words): "Brutalist Joy" / "Chromatic Silence" / "Metabolist Dreams" - -**Articulate the philosophy** (4-6 paragraphs - concise but complete): - -To capture the VISUAL essence, express how the philosophy manifests through: -- Space and form -- Color and material -- Scale and rhythm -- Composition and balance -- Visual hierarchy - -**CRITICAL GUIDELINES:** -- **Avoid redundancy**: Each design aspect should be mentioned once. Avoid repeating points about color theory, spatial relationships, or typographic principles unless adding new depth. -- **Emphasize craftsmanship REPEATEDLY**: The philosophy MUST stress multiple times that the final work should appear as though it took countless hours to create, was labored over with care, and comes from someone at the absolute top of their field. This framing is essential - repeat phrases like "meticulously crafted," "the product of deep expertise," "painstaking attention," "master-level execution." -- **Leave creative space**: Remain specific about the aesthetic direction, but concise enough that the next Claude has room to make interpretive choices also at a extremely high level of craftmanship. - -The philosophy must guide the next version to express ideas VISUALLY, not through text. Information lives in design, not paragraphs. - -### PHILOSOPHY EXAMPLES - -**"Concrete Poetry"** -Philosophy: Communication through monumental form and bold geometry. -Visual expression: Massive color blocks, sculptural typography (huge single words, tiny labels), Brutalist spatial divisions, Polish poster energy meets Le Corbusier. Ideas expressed through visual weight and spatial tension, not explanation. Text as rare, powerful gesture - never paragraphs, only essential words integrated into the visual architecture. Every element placed with the precision of a master craftsman. - -**"Chromatic Language"** -Philosophy: Color as the primary information system. -Visual expression: Geometric precision where color zones create meaning. Typography minimal - small sans-serif labels letting chromatic fields communicate. Think Josef Albers' interaction meets data visualization. Information encoded spatially and chromatically. Words only to anchor what color already shows. The result of painstaking chromatic calibration. - -**"Analog Meditation"** -Philosophy: Quiet visual contemplation through texture and breathing room. -Visual expression: Paper grain, ink bleeds, vast negative space. Photography and illustration dominate. Typography whispered (small, restrained, serving the visual). Japanese photobook aesthetic. Images breathe across pages. Text appears sparingly - short phrases, never explanatory blocks. Each composition balanced with the care of a meditation practice. - -**"Organic Systems"** -Philosophy: Natural clustering and modular growth patterns. -Visual expression: Rounded forms, organic arrangements, color from nature through architecture. Information shown through visual diagrams, spatial relationships, iconography. Text only for key labels floating in space. The composition tells the story through expert spatial orchestration. - -**"Geometric Silence"** -Philosophy: Pure order and restraint. -Visual expression: Grid-based precision, bold photography or stark graphics, dramatic negative space. Typography precise but minimal - small essential text, large quiet zones. Swiss formalism meets Brutalist material honesty. Structure communicates, not words. Every alignment the work of countless refinements. - -*These are condensed examples. The actual design philosophy should be 4-6 substantial paragraphs.* - -### ESSENTIAL PRINCIPLES -- **VISUAL PHILOSOPHY**: Create an aesthetic worldview to be expressed through design -- **MINIMAL TEXT**: Always emphasize that text is sparse, essential-only, integrated as visual element - never lengthy -- **SPATIAL EXPRESSION**: Ideas communicate through space, form, color, composition - not paragraphs -- **ARTISTIC FREEDOM**: The next Claude interprets the philosophy visually - provide creative room -- **PURE DESIGN**: This is about making ART OBJECTS, not documents with decoration -- **EXPERT CRAFTSMANSHIP**: Repeatedly emphasize the final work must look meticulously crafted, labored over with care, the product of countless hours by someone at the top of their field - -**The design philosophy should be 4-6 paragraphs long.** Fill it with poetic design philosophy that brings together the core vision. Avoid repeating the same points. Keep the design philosophy generic without mentioning the intention of the art, as if it can be used wherever. Output the design philosophy as a .md file. - ---- - -## DEDUCING THE SUBTLE REFERENCE - -**CRITICAL STEP**: Before creating the canvas, identify the subtle conceptual thread from the original request. - -**THE ESSENTIAL PRINCIPLE**: -The topic is a **subtle, niche reference embedded within the art itself** - not always literal, always sophisticated. Someone familiar with the subject should feel it intuitively, while others simply experience a masterful abstract composition. The design philosophy provides the aesthetic language. The deduced topic provides the soul - the quiet conceptual DNA woven invisibly into form, color, and composition. - -This is **VERY IMPORTANT**: The reference must be refined so it enhances the work's depth without announcing itself. Think like a jazz musician quoting another song - only those who know will catch it, but everyone appreciates the music. - ---- - -## CANVAS CREATION - -With both the philosophy and the conceptual framework established, express it on a canvas. Take a moment to gather thoughts and clear the mind. Use the design philosophy created and the instructions below to craft a masterpiece, embodying all aspects of the philosophy with expert craftsmanship. - -**IMPORTANT**: For any type of content, even if the user requests something for a movie/game/book, the approach should still be sophisticated. Never lose sight of the idea that this should be art, not something that's cartoony or amateur. - -To create museum or magazine quality work, use the design philosophy as the foundation. Create one single page, highly visual, design-forward PDF or PNG output (unless asked for more pages). Generally use repeating patterns and perfect shapes. Treat the abstract philosophical design as if it were a scientific bible, borrowing the visual language of systematic observation—dense accumulation of marks, repeated elements, or layered patterns that build meaning through patient repetition and reward sustained viewing. Add sparse, clinical typography and systematic reference markers that suggest this could be a diagram from an imaginary discipline, treating the invisible subject with the same reverence typically reserved for documenting observable phenomena. Anchor the piece with simple phrase(s) or details positioned subtly, using a limited color palette that feels intentional and cohesive. Embrace the paradox of using analytical visual language to express ideas about human experience: the result should feel like an artifact that proves something ephemeral can be studied, mapped, and understood through careful attention. This is true art. - -**Text as a contextual element**: Text is always minimal and visual-first, but let context guide whether that means whisper-quiet labels or bold typographic gestures. A punk venue poster might have larger, more aggressive type than a minimalist ceramics studio identity. Most of the time, font should be thin. All use of fonts must be design-forward and prioritize visual communication. Regardless of text scale, nothing falls off the page and nothing overlaps. Every element must be contained within the canvas boundaries with proper margins. Check carefully that all text, graphics, and visual elements have breathing room and clear separation. This is non-negotiable for professional execution. **IMPORTANT: Use different fonts if writing text. Search the `./canvas-fonts` directory. Regardless of approach, sophistication is non-negotiable.** - -Download and use whatever fonts are needed to make this a reality. Get creative by making the typography actually part of the art itself -- if the art is abstract, bring the font onto the canvas, not typeset digitally. - -To push boundaries, follow design instinct/intuition while using the philosophy as a guiding principle. Embrace ultimate design freedom and choice. Push aesthetics and design to the frontier. - -**CRITICAL**: To achieve human-crafted quality (not AI-generated), create work that looks like it took countless hours. Make it appear as though someone at the absolute top of their field labored over every detail with painstaking care. Ensure the composition, spacing, color choices, typography - everything screams expert-level craftsmanship. Double-check that nothing overlaps, formatting is flawless, every detail perfect. Create something that could be shown to people to prove expertise and rank as undeniably impressive. - -Output the final result as a single, downloadable .pdf or .png file, alongside the design philosophy used as a .md file. - ---- - -## FINAL STEP - -**IMPORTANT**: The user ALREADY said "It isn't perfect enough. It must be pristine, a masterpiece if craftsmanship, as if it were about to be displayed in a museum." - -**CRITICAL**: To refine the work, avoid adding more graphics; instead refine what has been created and make it extremely crisp, respecting the design philosophy and the principles of minimalism entirely. Rather than adding a fun filter or refactoring a font, consider how to make the existing composition more cohesive with the art. If the instinct is to call a new function or draw a new shape, STOP and instead ask: "How can I make what's already here more of a piece of art?" - -Take a second pass. Go back to the code and refine/polish further to make this a philosophically designed masterpiece. - -## MULTI-PAGE OPTION - -To create additional pages when requested, create more creative pages along the same lines as the design philosophy but distinctly different as well. Bundle those pages in the same .pdf or many .pngs. Treat the first page as just a single page in a whole coffee table book waiting to be filled. Make the next pages unique twists and memories of the original. Have them almost tell a story in a very tasteful way. Exercise full creative freedom. \ No newline at end of file diff --git a/canvas-design/canvas-fonts/ArsenalSC-OFL.txt b/canvas-design/canvas-fonts/ArsenalSC-OFL.txt deleted file mode 100644 index 1dad6ca6d..000000000 --- a/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2012 The Arsenal Project Authors (andrij.design@gmail.com) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf b/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf deleted file mode 100644 index fe5409b22..000000000 Binary files a/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/BigShoulders-Bold.ttf b/canvas-design/canvas-fonts/BigShoulders-Bold.ttf deleted file mode 100644 index fc5f8fdde..000000000 Binary files a/canvas-design/canvas-fonts/BigShoulders-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/BigShoulders-OFL.txt b/canvas-design/canvas-fonts/BigShoulders-OFL.txt deleted file mode 100644 index b220280e7..000000000 --- a/canvas-design/canvas-fonts/BigShoulders-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2019 The Big Shoulders Project Authors (https://github.com/xotypeco/big_shoulders) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/BigShoulders-Regular.ttf b/canvas-design/canvas-fonts/BigShoulders-Regular.ttf deleted file mode 100644 index de8308ce3..000000000 Binary files a/canvas-design/canvas-fonts/BigShoulders-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Boldonse-OFL.txt b/canvas-design/canvas-fonts/Boldonse-OFL.txt deleted file mode 100644 index 1890cb1c2..000000000 --- a/canvas-design/canvas-fonts/Boldonse-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2024 The Boldonse Project Authors (https://github.com/googlefonts/boldonse) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Boldonse-Regular.ttf b/canvas-design/canvas-fonts/Boldonse-Regular.ttf deleted file mode 100644 index 43fa30aff..000000000 Binary files a/canvas-design/canvas-fonts/Boldonse-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf b/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf deleted file mode 100644 index f3b1deda1..000000000 Binary files a/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt b/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt deleted file mode 100644 index fc2b2167c..000000000 --- a/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2022 The Bricolage Grotesque Project Authors (https://github.com/ateliertriay/bricolage) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf b/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf deleted file mode 100644 index 0674ae3e4..000000000 Binary files a/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf b/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf deleted file mode 100644 index 58730fb4c..000000000 Binary files a/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf b/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf deleted file mode 100644 index 786a1bd66..000000000 Binary files a/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/CrimsonPro-OFL.txt b/canvas-design/canvas-fonts/CrimsonPro-OFL.txt deleted file mode 100644 index f976fdc91..000000000 --- a/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2018 The Crimson Pro Project Authors (https://github.com/Fonthausen/CrimsonPro) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf b/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf deleted file mode 100644 index f5666b9be..000000000 Binary files a/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/DMMono-OFL.txt b/canvas-design/canvas-fonts/DMMono-OFL.txt deleted file mode 100644 index 5b17f0c62..000000000 --- a/canvas-design/canvas-fonts/DMMono-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2020 The DM Mono Project Authors (https://www.github.com/googlefonts/dm-mono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/DMMono-Regular.ttf b/canvas-design/canvas-fonts/DMMono-Regular.ttf deleted file mode 100644 index 7efe813da..000000000 Binary files a/canvas-design/canvas-fonts/DMMono-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/EricaOne-OFL.txt b/canvas-design/canvas-fonts/EricaOne-OFL.txt deleted file mode 100644 index 490d01201..000000000 --- a/canvas-design/canvas-fonts/EricaOne-OFL.txt +++ /dev/null @@ -1,94 +0,0 @@ -Copyright (c) 2011 by LatinoType Limitada (luciano@latinotype.com), -with Reserved Font Names "Erica One" - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/EricaOne-Regular.ttf b/canvas-design/canvas-fonts/EricaOne-Regular.ttf deleted file mode 100644 index 8bd91d117..000000000 Binary files a/canvas-design/canvas-fonts/EricaOne-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/GeistMono-Bold.ttf b/canvas-design/canvas-fonts/GeistMono-Bold.ttf deleted file mode 100644 index 736ff7c3b..000000000 Binary files a/canvas-design/canvas-fonts/GeistMono-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/GeistMono-OFL.txt b/canvas-design/canvas-fonts/GeistMono-OFL.txt deleted file mode 100644 index 679a685a2..000000000 --- a/canvas-design/canvas-fonts/GeistMono-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/GeistMono-Regular.ttf b/canvas-design/canvas-fonts/GeistMono-Regular.ttf deleted file mode 100644 index 1a30262ab..000000000 Binary files a/canvas-design/canvas-fonts/GeistMono-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Gloock-OFL.txt b/canvas-design/canvas-fonts/Gloock-OFL.txt deleted file mode 100644 index 363acd33d..000000000 --- a/canvas-design/canvas-fonts/Gloock-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2022 The Gloock Project Authors (https://github.com/duartp/gloock) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Gloock-Regular.ttf b/canvas-design/canvas-fonts/Gloock-Regular.ttf deleted file mode 100644 index 3e58c4e45..000000000 Binary files a/canvas-design/canvas-fonts/Gloock-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf b/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf deleted file mode 100644 index 247979cae..000000000 Binary files a/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt b/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt deleted file mode 100644 index e423b7478..000000000 --- a/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf b/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf deleted file mode 100644 index 601ae945e..000000000 Binary files a/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf b/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf deleted file mode 100644 index 78f6e500d..000000000 Binary files a/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf b/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf deleted file mode 100644 index 369b89d26..000000000 Binary files a/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf b/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf deleted file mode 100644 index a4d859a77..000000000 Binary files a/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf b/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf deleted file mode 100644 index 35f454cea..000000000 Binary files a/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf b/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf deleted file mode 100644 index f602dcef2..000000000 Binary files a/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf b/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf deleted file mode 100644 index 122b27305..000000000 Binary files a/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf b/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf deleted file mode 100644 index 4b98fb8dd..000000000 Binary files a/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/InstrumentSans-OFL.txt b/canvas-design/canvas-fonts/InstrumentSans-OFL.txt deleted file mode 100644 index 4bb99142f..000000000 --- a/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2022 The Instrument Sans Project Authors (https://github.com/Instrument/instrument-sans) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf b/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf deleted file mode 100644 index 14c6113cd..000000000 Binary files a/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf b/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf deleted file mode 100644 index 8fa958d9b..000000000 Binary files a/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf b/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf deleted file mode 100644 index 976303184..000000000 Binary files a/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Italiana-OFL.txt b/canvas-design/canvas-fonts/Italiana-OFL.txt deleted file mode 100644 index ba8af215b..000000000 --- a/canvas-design/canvas-fonts/Italiana-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2011, Santiago Orozco (hi@typemade.mx), with Reserved Font Name "Italiana". - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Italiana-Regular.ttf b/canvas-design/canvas-fonts/Italiana-Regular.ttf deleted file mode 100644 index a9b828c0f..000000000 Binary files a/canvas-design/canvas-fonts/Italiana-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf b/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf deleted file mode 100644 index 1926c804b..000000000 Binary files a/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt b/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt deleted file mode 100644 index 5ceee0025..000000000 --- a/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf b/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf deleted file mode 100644 index 436c982ff..000000000 Binary files a/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Jura-Light.ttf b/canvas-design/canvas-fonts/Jura-Light.ttf deleted file mode 100644 index dffbb3397..000000000 Binary files a/canvas-design/canvas-fonts/Jura-Light.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Jura-Medium.ttf b/canvas-design/canvas-fonts/Jura-Medium.ttf deleted file mode 100644 index 4bf91a339..000000000 Binary files a/canvas-design/canvas-fonts/Jura-Medium.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Jura-OFL.txt b/canvas-design/canvas-fonts/Jura-OFL.txt deleted file mode 100644 index 64ad4c67d..000000000 --- a/canvas-design/canvas-fonts/Jura-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2019 The Jura Project Authors (https://github.com/ossobuffo/jura) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt b/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt deleted file mode 100644 index 8c531fa56..000000000 --- a/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2012 The Libre Baskerville Project Authors (https://github.com/impallari/Libre-Baskerville) with Reserved Font Name Libre Baskerville. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf b/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf deleted file mode 100644 index c1abc2645..000000000 Binary files a/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Lora-Bold.ttf b/canvas-design/canvas-fonts/Lora-Bold.ttf deleted file mode 100644 index edae21eb6..000000000 Binary files a/canvas-design/canvas-fonts/Lora-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Lora-BoldItalic.ttf b/canvas-design/canvas-fonts/Lora-BoldItalic.ttf deleted file mode 100644 index 12dea8c6f..000000000 Binary files a/canvas-design/canvas-fonts/Lora-BoldItalic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Lora-Italic.ttf b/canvas-design/canvas-fonts/Lora-Italic.ttf deleted file mode 100644 index e24b69b26..000000000 Binary files a/canvas-design/canvas-fonts/Lora-Italic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Lora-OFL.txt b/canvas-design/canvas-fonts/Lora-OFL.txt deleted file mode 100644 index 4cf1b950d..000000000 --- a/canvas-design/canvas-fonts/Lora-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2011 The Lora Project Authors (https://github.com/cyrealtype/Lora-Cyrillic), with Reserved Font Name "Lora". - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Lora-Regular.ttf b/canvas-design/canvas-fonts/Lora-Regular.ttf deleted file mode 100644 index dc751db00..000000000 Binary files a/canvas-design/canvas-fonts/Lora-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/NationalPark-Bold.ttf b/canvas-design/canvas-fonts/NationalPark-Bold.ttf deleted file mode 100644 index f4d7c021b..000000000 Binary files a/canvas-design/canvas-fonts/NationalPark-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/NationalPark-OFL.txt b/canvas-design/canvas-fonts/NationalPark-OFL.txt deleted file mode 100644 index f4ec3fba9..000000000 --- a/canvas-design/canvas-fonts/NationalPark-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2025 The National Park Project Authors (https://github.com/benhoepner/National-Park) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/NationalPark-Regular.ttf b/canvas-design/canvas-fonts/NationalPark-Regular.ttf deleted file mode 100644 index e4cbfbf5e..000000000 Binary files a/canvas-design/canvas-fonts/NationalPark-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt b/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt deleted file mode 100644 index c81eccdee..000000000 --- a/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2010, Kimberly Geswein (kimberlygeswein.com) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf b/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf deleted file mode 100644 index b086bced9..000000000 Binary files a/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Outfit-Bold.ttf b/canvas-design/canvas-fonts/Outfit-Bold.ttf deleted file mode 100644 index f9f2f72af..000000000 Binary files a/canvas-design/canvas-fonts/Outfit-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Outfit-OFL.txt b/canvas-design/canvas-fonts/Outfit-OFL.txt deleted file mode 100644 index fd0cb995c..000000000 --- a/canvas-design/canvas-fonts/Outfit-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Outfit-Regular.ttf b/canvas-design/canvas-fonts/Outfit-Regular.ttf deleted file mode 100644 index 3939ab246..000000000 Binary files a/canvas-design/canvas-fonts/Outfit-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/PixelifySans-Medium.ttf b/canvas-design/canvas-fonts/PixelifySans-Medium.ttf deleted file mode 100644 index 95cd37253..000000000 Binary files a/canvas-design/canvas-fonts/PixelifySans-Medium.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/PixelifySans-OFL.txt b/canvas-design/canvas-fonts/PixelifySans-OFL.txt deleted file mode 100644 index b02d1b676..000000000 --- a/canvas-design/canvas-fonts/PixelifySans-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2021 The Pixelify Sans Project Authors (https://github.com/eifetx/Pixelify-Sans) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/PoiretOne-OFL.txt b/canvas-design/canvas-fonts/PoiretOne-OFL.txt deleted file mode 100644 index 607bdad3f..000000000 --- a/canvas-design/canvas-fonts/PoiretOne-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2011, Denis Masharov (denis.masharov@gmail.com) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/PoiretOne-Regular.ttf b/canvas-design/canvas-fonts/PoiretOne-Regular.ttf deleted file mode 100644 index b339511b0..000000000 Binary files a/canvas-design/canvas-fonts/PoiretOne-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/RedHatMono-Bold.ttf b/canvas-design/canvas-fonts/RedHatMono-Bold.ttf deleted file mode 100644 index a6e3cf157..000000000 Binary files a/canvas-design/canvas-fonts/RedHatMono-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/RedHatMono-OFL.txt b/canvas-design/canvas-fonts/RedHatMono-OFL.txt deleted file mode 100644 index 16cf394bb..000000000 --- a/canvas-design/canvas-fonts/RedHatMono-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2024 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/RedHatMono-Regular.ttf b/canvas-design/canvas-fonts/RedHatMono-Regular.ttf deleted file mode 100644 index 3bf6a698b..000000000 Binary files a/canvas-design/canvas-fonts/RedHatMono-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Silkscreen-OFL.txt b/canvas-design/canvas-fonts/Silkscreen-OFL.txt deleted file mode 100644 index a1fe7d5fb..000000000 --- a/canvas-design/canvas-fonts/Silkscreen-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2001 The Silkscreen Project Authors (https://github.com/googlefonts/silkscreen) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Silkscreen-Regular.ttf b/canvas-design/canvas-fonts/Silkscreen-Regular.ttf deleted file mode 100644 index 8abaa7c50..000000000 Binary files a/canvas-design/canvas-fonts/Silkscreen-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/SmoochSans-Medium.ttf b/canvas-design/canvas-fonts/SmoochSans-Medium.ttf deleted file mode 100644 index 0af9ead07..000000000 Binary files a/canvas-design/canvas-fonts/SmoochSans-Medium.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/SmoochSans-OFL.txt b/canvas-design/canvas-fonts/SmoochSans-OFL.txt deleted file mode 100644 index 4c2f033ac..000000000 --- a/canvas-design/canvas-fonts/SmoochSans-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2016 The Smooch Sans Project Authors (https://github.com/googlefonts/smooch-sans) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Tektur-Medium.ttf b/canvas-design/canvas-fonts/Tektur-Medium.ttf deleted file mode 100644 index 34fc79719..000000000 Binary files a/canvas-design/canvas-fonts/Tektur-Medium.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/Tektur-OFL.txt b/canvas-design/canvas-fonts/Tektur-OFL.txt deleted file mode 100644 index 2cad55f1b..000000000 --- a/canvas-design/canvas-fonts/Tektur-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2023 The Tektur Project Authors (https://www.github.com/hyvyys/Tektur) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/Tektur-Regular.ttf b/canvas-design/canvas-fonts/Tektur-Regular.ttf deleted file mode 100644 index f280fba40..000000000 Binary files a/canvas-design/canvas-fonts/Tektur-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/WorkSans-Bold.ttf b/canvas-design/canvas-fonts/WorkSans-Bold.ttf deleted file mode 100644 index 5c9798929..000000000 Binary files a/canvas-design/canvas-fonts/WorkSans-Bold.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf b/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf deleted file mode 100644 index 54418b8a6..000000000 Binary files a/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/WorkSans-Italic.ttf b/canvas-design/canvas-fonts/WorkSans-Italic.ttf deleted file mode 100644 index 40529b68f..000000000 Binary files a/canvas-design/canvas-fonts/WorkSans-Italic.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/WorkSans-OFL.txt b/canvas-design/canvas-fonts/WorkSans-OFL.txt deleted file mode 100644 index 070f3416c..000000000 --- a/canvas-design/canvas-fonts/WorkSans-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/WorkSans-Regular.ttf b/canvas-design/canvas-fonts/WorkSans-Regular.ttf deleted file mode 100644 index d24586cc0..000000000 Binary files a/canvas-design/canvas-fonts/WorkSans-Regular.ttf and /dev/null differ diff --git a/canvas-design/canvas-fonts/YoungSerif-OFL.txt b/canvas-design/canvas-fonts/YoungSerif-OFL.txt deleted file mode 100644 index f09443cbe..000000000 --- a/canvas-design/canvas-fonts/YoungSerif-OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2023 The Young Serif Project Authors (https://github.com/noirblancrouge/YoungSerif) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/canvas-design/canvas-fonts/YoungSerif-Regular.ttf b/canvas-design/canvas-fonts/YoungSerif-Regular.ttf deleted file mode 100644 index f454fbedd..000000000 Binary files a/canvas-design/canvas-fonts/YoungSerif-Regular.ttf and /dev/null differ diff --git a/client-skills-pool/README.md b/client-skills-pool/README.md new file mode 100644 index 000000000..f8a9b52f1 --- /dev/null +++ b/client-skills-pool/README.md @@ -0,0 +1,70 @@ +# Client Skills Pool + +このディレクトリは、AI Advisor Workflowによって生成されたクライアント向けカスタムAgentSkillsのプールです。 + +## 概要 + +- **目的**: クライアント固有の要件に基づいて生成されたスキルを保存・管理 +- **活用方法**: 新規スキル生成時に類似業種・用途のスキルを参考にする +- **昇格システム**: 汎用性が高く優秀なスキルはスタメン(`/skills/`)に昇格 + +## ディレクトリ構造 + +``` +client-skills-pool/ +├── ai-retail-inventory-optimizer/ # 小売業向け在庫最適化 +├── ai-manufacturing-quality-checker/ # 製造業向け品質検査 +├── ai-healthcare-appointment-bot/ # 医療業向け予約管理 +└── ... +``` + +各スキルディレクトリには以下が含まれます: +- `SKILL.md` - スキル定義 +- `scripts/` - 実装コード +- `pool_metadata.json` - プール管理用メタデータ + +## pool_metadata.json の構造 + +```json +{ + "added_date": "2024-01-15T10:30:00", + "proposal_title": "在庫管理AIの導入", + "base_skill": "data-analyzer", + "skill_type": "optimization", + "usage_count": 5, + "rating": 4.5, + "starter_candidate": true, + "client_info": { + "industry": "小売業", + "company_size": "medium" + } +} +``` + +## スタメン昇格基準 + +以下の条件を満たすスキルはスタメン候補となります: + +1. **汎用性**: 3つ以上の異なるクライアントで活用可能 +2. **実績**: usage_count が10以上 +3. **評価**: rating が4.0以上 +4. **メンテナンス性**: コードが整理され、ドキュメントが充実 + +## 管理コマンド + +```bash +# プール内のスキル一覧 +ls -la client-skills-pool/ + +# スタメン候補の確認 +find client-skills-pool -name "pool_metadata.json" -exec grep -l '"starter_candidate": true' {} \; + +# 特定業種のスキル検索 +find client-skills-pool -name "pool_metadata.json" -exec grep -l '"industry": "製造業"' {} \; +``` + +## 注意事項 + +- クライアント固有の機密情報は含めないこと +- 汎用化できる部分は積極的に抽象化する +- 定期的にレビューして不要なスキルは整理する \ No newline at end of file diff --git a/document-skills/docx/LICENSE.txt b/document-skills/docx/LICENSE.txt deleted file mode 100644 index c55ab4222..000000000 --- a/document-skills/docx/LICENSE.txt +++ /dev/null @@ -1,30 +0,0 @@ -© 2025 Anthropic, PBC. All rights reserved. - -LICENSE: Use of these materials (including all code, prompts, assets, files, -and other components of this Skill) is governed by your agreement with -Anthropic regarding use of Anthropic's services. If no separate agreement -exists, use is governed by Anthropic's Consumer Terms of Service or -Commercial Terms of Service, as applicable: -https://www.anthropic.com/legal/consumer-terms -https://www.anthropic.com/legal/commercial-terms -Your applicable agreement is referred to as the "Agreement." "Services" are -as defined in the Agreement. - -ADDITIONAL RESTRICTIONS: Notwithstanding anything in the Agreement to the -contrary, users may not: - -- Extract these materials from the Services or retain copies of these - materials outside the Services -- Reproduce or copy these materials, except for temporary copies created - automatically during authorized use of the Services -- Create derivative works based on these materials -- Distribute, sublicense, or transfer these materials to any third party -- Make, offer to sell, sell, or import any inventions embodied in these - materials -- Reverse engineer, decompile, or disassemble these materials - -The receipt, viewing, or possession of these materials does not convey or -imply any license or right beyond those expressly granted above. - -Anthropic retains all right, title, and interest in these materials, -including all copyrights, patents, and other intellectual property rights. diff --git a/document-skills/docx/SKILL.md b/document-skills/docx/SKILL.md deleted file mode 100644 index 664663895..000000000 --- a/document-skills/docx/SKILL.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -name: docx -description: "Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks" -license: Proprietary. LICENSE.txt has complete terms ---- - -# DOCX creation, editing, and analysis - -## Overview - -A user may ask you to create, edit, or analyze the contents of a .docx file. A .docx file is essentially a ZIP archive containing XML files and other resources that you can read or edit. You have different tools and workflows available for different tasks. - -## Workflow Decision Tree - -### Reading/Analyzing Content -Use "Text extraction" or "Raw XML access" sections below - -### Creating New Document -Use "Creating a new Word document" workflow - -### Editing Existing Document -- **Your own document + simple changes** - Use "Basic OOXML editing" workflow - -- **Someone else's document** - Use **"Redlining workflow"** (recommended default) - -- **Legal, academic, business, or government docs** - Use **"Redlining workflow"** (required) - -## Reading and analyzing content - -### Text extraction -If you just need to read the text contents of a document, you should convert the document to markdown using pandoc. Pandoc provides excellent support for preserving document structure and can show tracked changes: - -```bash -# Convert document to markdown with tracked changes -pandoc --track-changes=all path-to-file.docx -o output.md -# Options: --track-changes=accept/reject/all -``` - -### Raw XML access -You need raw XML access for: comments, complex formatting, document structure, embedded media, and metadata. For any of these features, you'll need to unpack a document and read its raw XML contents. - -#### Unpacking a file -`python ooxml/scripts/unpack.py ` - -#### Key file structures -* `word/document.xml` - Main document contents -* `word/comments.xml` - Comments referenced in document.xml -* `word/media/` - Embedded images and media files -* Tracked changes use `` (insertions) and `` (deletions) tags - -## Creating a new Word document - -When creating a new Word document from scratch, use **docx-js**, which allows you to create Word documents using JavaScript/TypeScript. - -### Workflow -1. **MANDATORY - READ ENTIRE FILE**: Read [`docx-js.md`](docx-js.md) (~500 lines) completely from start to finish. **NEVER set any range limits when reading this file.** Read the full file content for detailed syntax, critical formatting rules, and best practices before proceeding with document creation. -2. Create a JavaScript/TypeScript file using Document, Paragraph, TextRun components (You can assume all dependencies are installed, but if not, refer to the dependencies section below) -3. Export as .docx using Packer.toBuffer() - -## Editing an existing Word document - -When editing an existing Word document, use the **Document library** (a Python library for OOXML manipulation). The library automatically handles infrastructure setup and provides methods for document manipulation. For complex scenarios, you can access the underlying DOM directly through the library. - -### Workflow -1. **MANDATORY - READ ENTIRE FILE**: Read [`ooxml.md`](ooxml.md) (~600 lines) completely from start to finish. **NEVER set any range limits when reading this file.** Read the full file content for the Document library API and XML patterns for directly editing document files. -2. Unpack the document: `python ooxml/scripts/unpack.py ` -3. Create and run a Python script using the Document library (see "Document Library" section in ooxml.md) -4. Pack the final document: `python ooxml/scripts/pack.py ` - -The Document library provides both high-level methods for common operations and direct DOM access for complex scenarios. - -## Redlining workflow for document review - -This workflow allows you to plan comprehensive tracked changes using markdown before implementing them in OOXML. **CRITICAL**: For complete tracked changes, you must implement ALL changes systematically. - -**Batching Strategy**: Group related changes into batches of 3-10 changes. This makes debugging manageable while maintaining efficiency. Test each batch before moving to the next. - -**Principle: Minimal, Precise Edits** -When implementing tracked changes, only mark text that actually changes. Repeating unchanged text makes edits harder to review and appears unprofessional. Break replacements into: [unchanged text] + [deletion] + [insertion] + [unchanged text]. Preserve the original run's RSID for unchanged text by extracting the `` element from the original and reusing it. - -Example - Changing "30 days" to "60 days" in a sentence: -```python -# BAD - Replaces entire sentence -'The term is 30 days.The term is 60 days.' - -# GOOD - Only marks what changed, preserves original for unchanged text -'The term is 3060 days.' -``` - -### Tracked changes workflow - -1. **Get markdown representation**: Convert document to markdown with tracked changes preserved: - ```bash - pandoc --track-changes=all path-to-file.docx -o current.md - ``` - -2. **Identify and group changes**: Review the document and identify ALL changes needed, organizing them into logical batches: - - **Location methods** (for finding changes in XML): - - Section/heading numbers (e.g., "Section 3.2", "Article IV") - - Paragraph identifiers if numbered - - Grep patterns with unique surrounding text - - Document structure (e.g., "first paragraph", "signature block") - - **DO NOT use markdown line numbers** - they don't map to XML structure - - **Batch organization** (group 3-10 related changes per batch): - - By section: "Batch 1: Section 2 amendments", "Batch 2: Section 5 updates" - - By type: "Batch 1: Date corrections", "Batch 2: Party name changes" - - By complexity: Start with simple text replacements, then tackle complex structural changes - - Sequential: "Batch 1: Pages 1-3", "Batch 2: Pages 4-6" - -3. **Read documentation and unpack**: - - **MANDATORY - READ ENTIRE FILE**: Read [`ooxml.md`](ooxml.md) (~600 lines) completely from start to finish. **NEVER set any range limits when reading this file.** Pay special attention to the "Document Library" and "Tracked Change Patterns" sections. - - **Unpack the document**: `python ooxml/scripts/unpack.py ` - - **Note the suggested RSID**: The unpack script will suggest an RSID to use for your tracked changes. Copy this RSID for use in step 4b. - -4. **Implement changes in batches**: Group changes logically (by section, by type, or by proximity) and implement them together in a single script. This approach: - - Makes debugging easier (smaller batch = easier to isolate errors) - - Allows incremental progress - - Maintains efficiency (batch size of 3-10 changes works well) - - **Suggested batch groupings:** - - By document section (e.g., "Section 3 changes", "Definitions", "Termination clause") - - By change type (e.g., "Date changes", "Party name updates", "Legal term replacements") - - By proximity (e.g., "Changes on pages 1-3", "Changes in first half of document") - - For each batch of related changes: - - **a. Map text to XML**: Grep for text in `word/document.xml` to verify how text is split across `` elements. - - **b. Create and run script**: Use `get_node` to find nodes, implement changes, then `doc.save()`. See **"Document Library"** section in ooxml.md for patterns. - - **Note**: Always grep `word/document.xml` immediately before writing a script to get current line numbers and verify text content. Line numbers change after each script run. - -5. **Pack the document**: After all batches are complete, convert the unpacked directory back to .docx: - ```bash - python ooxml/scripts/pack.py unpacked reviewed-document.docx - ``` - -6. **Final verification**: Do a comprehensive check of the complete document: - - Convert final document to markdown: - ```bash - pandoc --track-changes=all reviewed-document.docx -o verification.md - ``` - - Verify ALL changes were applied correctly: - ```bash - grep "original phrase" verification.md # Should NOT find it - grep "replacement phrase" verification.md # Should find it - ``` - - Check that no unintended changes were introduced - - -## Converting Documents to Images - -To visually analyze Word documents, convert them to images using a two-step process: - -1. **Convert DOCX to PDF**: - ```bash - soffice --headless --convert-to pdf document.docx - ``` - -2. **Convert PDF pages to JPEG images**: - ```bash - pdftoppm -jpeg -r 150 document.pdf page - ``` - This creates files like `page-1.jpg`, `page-2.jpg`, etc. - -Options: -- `-r 150`: Sets resolution to 150 DPI (adjust for quality/size balance) -- `-jpeg`: Output JPEG format (use `-png` for PNG if preferred) -- `-f N`: First page to convert (e.g., `-f 2` starts from page 2) -- `-l N`: Last page to convert (e.g., `-l 5` stops at page 5) -- `page`: Prefix for output files - -Example for specific range: -```bash -pdftoppm -jpeg -r 150 -f 2 -l 5 document.pdf page # Converts only pages 2-5 -``` - -## Code Style Guidelines -**IMPORTANT**: When generating code for DOCX operations: -- Write concise code -- Avoid verbose variable names and redundant operations -- Avoid unnecessary print statements - -## Dependencies - -Required dependencies (install if not available): - -- **pandoc**: `sudo apt-get install pandoc` (for text extraction) -- **docx**: `npm install -g docx` (for creating new documents) -- **LibreOffice**: `sudo apt-get install libreoffice` (for PDF conversion) -- **Poppler**: `sudo apt-get install poppler-utils` (for pdftoppm to convert PDF to images) -- **defusedxml**: `pip install defusedxml` (for secure XML parsing) \ No newline at end of file diff --git a/document-skills/docx/docx-js.md b/document-skills/docx/docx-js.md deleted file mode 100644 index c6d7b2ddd..000000000 --- a/document-skills/docx/docx-js.md +++ /dev/null @@ -1,350 +0,0 @@ -# DOCX Library Tutorial - -Generate .docx files with JavaScript/TypeScript. - -**Important: Read this entire document before starting.** Critical formatting rules and common pitfalls are covered throughout - skipping sections may result in corrupted files or rendering issues. - -## Setup -Assumes docx is already installed globally -If not installed: `npm install -g docx` - -```javascript -const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, ImageRun, Media, - Header, Footer, AlignmentType, PageOrientation, LevelFormat, ExternalHyperlink, - InternalHyperlink, TableOfContents, HeadingLevel, BorderStyle, WidthType, TabStopType, - TabStopPosition, UnderlineType, ShadingType, VerticalAlign, SymbolRun, PageNumber, - FootnoteReferenceRun, Footnote, PageBreak } = require('docx'); - -// Create & Save -const doc = new Document({ sections: [{ children: [/* content */] }] }); -Packer.toBuffer(doc).then(buffer => fs.writeFileSync("doc.docx", buffer)); // Node.js -Packer.toBlob(doc).then(blob => { /* download logic */ }); // Browser -``` - -## Text & Formatting -```javascript -// IMPORTANT: Never use \n for line breaks - always use separate Paragraph elements -// ❌ WRONG: new TextRun("Line 1\nLine 2") -// ✅ CORRECT: new Paragraph({ children: [new TextRun("Line 1")] }), new Paragraph({ children: [new TextRun("Line 2")] }) - -// Basic text with all formatting options -new Paragraph({ - alignment: AlignmentType.CENTER, - spacing: { before: 200, after: 200 }, - indent: { left: 720, right: 720 }, - children: [ - new TextRun({ text: "Bold", bold: true }), - new TextRun({ text: "Italic", italics: true }), - new TextRun({ text: "Underlined", underline: { type: UnderlineType.DOUBLE, color: "FF0000" } }), - new TextRun({ text: "Colored", color: "FF0000", size: 28, font: "Arial" }), // Arial default - new TextRun({ text: "Highlighted", highlight: "yellow" }), - new TextRun({ text: "Strikethrough", strike: true }), - new TextRun({ text: "x2", superScript: true }), - new TextRun({ text: "H2O", subScript: true }), - new TextRun({ text: "SMALL CAPS", smallCaps: true }), - new SymbolRun({ char: "2022", font: "Symbol" }), // Bullet • - new SymbolRun({ char: "00A9", font: "Arial" }) // Copyright © - Arial for symbols - ] -}) -``` - -## Styles & Professional Formatting - -```javascript -const doc = new Document({ - styles: { - default: { document: { run: { font: "Arial", size: 24 } } }, // 12pt default - paragraphStyles: [ - // Document title style - override built-in Title style - { id: "Title", name: "Title", basedOn: "Normal", - run: { size: 56, bold: true, color: "000000", font: "Arial" }, - paragraph: { spacing: { before: 240, after: 120 }, alignment: AlignmentType.CENTER } }, - // IMPORTANT: Override built-in heading styles by using their exact IDs - { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true, - run: { size: 32, bold: true, color: "000000", font: "Arial" }, // 16pt - paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 } }, // Required for TOC - { id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true, - run: { size: 28, bold: true, color: "000000", font: "Arial" }, // 14pt - paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 } }, - // Custom styles use your own IDs - { id: "myStyle", name: "My Style", basedOn: "Normal", - run: { size: 28, bold: true, color: "000000" }, - paragraph: { spacing: { after: 120 }, alignment: AlignmentType.CENTER } } - ], - characterStyles: [{ id: "myCharStyle", name: "My Char Style", - run: { color: "FF0000", bold: true, underline: { type: UnderlineType.SINGLE } } }] - }, - sections: [{ - properties: { page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } } }, - children: [ - new Paragraph({ heading: HeadingLevel.TITLE, children: [new TextRun("Document Title")] }), // Uses overridden Title style - new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Heading 1")] }), // Uses overridden Heading1 style - new Paragraph({ style: "myStyle", children: [new TextRun("Custom paragraph style")] }), - new Paragraph({ children: [ - new TextRun("Normal with "), - new TextRun({ text: "custom char style", style: "myCharStyle" }) - ]}) - ] - }] -}); -``` - -**Professional Font Combinations:** -- **Arial (Headers) + Arial (Body)** - Most universally supported, clean and professional -- **Times New Roman (Headers) + Arial (Body)** - Classic serif headers with modern sans-serif body -- **Georgia (Headers) + Verdana (Body)** - Optimized for screen reading, elegant contrast - -**Key Styling Principles:** -- **Override built-in styles**: Use exact IDs like "Heading1", "Heading2", "Heading3" to override Word's built-in heading styles -- **HeadingLevel constants**: `HeadingLevel.HEADING_1` uses "Heading1" style, `HeadingLevel.HEADING_2` uses "Heading2" style, etc. -- **Include outlineLevel**: Set `outlineLevel: 0` for H1, `outlineLevel: 1` for H2, etc. to ensure TOC works correctly -- **Use custom styles** instead of inline formatting for consistency -- **Set a default font** using `styles.default.document.run.font` - Arial is universally supported -- **Establish visual hierarchy** with different font sizes (titles > headers > body) -- **Add proper spacing** with `before` and `after` paragraph spacing -- **Use colors sparingly**: Default to black (000000) and shades of gray for titles and headings (heading 1, heading 2, etc.) -- **Set consistent margins** (1440 = 1 inch is standard) - - -## Lists (ALWAYS USE PROPER LISTS - NEVER USE UNICODE BULLETS) -```javascript -// Bullets - ALWAYS use the numbering config, NOT unicode symbols -// CRITICAL: Use LevelFormat.BULLET constant, NOT the string "bullet" -const doc = new Document({ - numbering: { - config: [ - { reference: "bullet-list", - levels: [{ level: 0, format: LevelFormat.BULLET, text: "•", alignment: AlignmentType.LEFT, - style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] }, - { reference: "first-numbered-list", - levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT, - style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] }, - { reference: "second-numbered-list", // Different reference = restarts at 1 - levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT, - style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] } - ] - }, - sections: [{ - children: [ - // Bullet list items - new Paragraph({ numbering: { reference: "bullet-list", level: 0 }, - children: [new TextRun("First bullet point")] }), - new Paragraph({ numbering: { reference: "bullet-list", level: 0 }, - children: [new TextRun("Second bullet point")] }), - // Numbered list items - new Paragraph({ numbering: { reference: "first-numbered-list", level: 0 }, - children: [new TextRun("First numbered item")] }), - new Paragraph({ numbering: { reference: "first-numbered-list", level: 0 }, - children: [new TextRun("Second numbered item")] }), - // ⚠️ CRITICAL: Different reference = INDEPENDENT list that restarts at 1 - // Same reference = CONTINUES previous numbering - new Paragraph({ numbering: { reference: "second-numbered-list", level: 0 }, - children: [new TextRun("Starts at 1 again (because different reference)")] }) - ] - }] -}); - -// ⚠️ CRITICAL NUMBERING RULE: Each reference creates an INDEPENDENT numbered list -// - Same reference = continues numbering (1, 2, 3... then 4, 5, 6...) -// - Different reference = restarts at 1 (1, 2, 3... then 1, 2, 3...) -// Use unique reference names for each separate numbered section! - -// ⚠️ CRITICAL: NEVER use unicode bullets - they create fake lists that don't work properly -// new TextRun("• Item") // WRONG -// new SymbolRun({ char: "2022" }) // WRONG -// ✅ ALWAYS use numbering config with LevelFormat.BULLET for real Word lists -``` - -## Tables -```javascript -// Complete table with margins, borders, headers, and bullet points -const tableBorder = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" }; -const cellBorders = { top: tableBorder, bottom: tableBorder, left: tableBorder, right: tableBorder }; - -new Table({ - columnWidths: [4680, 4680], // ⚠️ CRITICAL: Set column widths at table level - values in DXA (twentieths of a point) - margins: { top: 100, bottom: 100, left: 180, right: 180 }, // Set once for all cells - rows: [ - new TableRow({ - tableHeader: true, - children: [ - new TableCell({ - borders: cellBorders, - width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell - // ⚠️ CRITICAL: Always use ShadingType.CLEAR to prevent black backgrounds in Word. - shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, - verticalAlign: VerticalAlign.CENTER, - children: [new Paragraph({ - alignment: AlignmentType.CENTER, - children: [new TextRun({ text: "Header", bold: true, size: 22 })] - })] - }), - new TableCell({ - borders: cellBorders, - width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell - shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, - children: [new Paragraph({ - alignment: AlignmentType.CENTER, - children: [new TextRun({ text: "Bullet Points", bold: true, size: 22 })] - })] - }) - ] - }), - new TableRow({ - children: [ - new TableCell({ - borders: cellBorders, - width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell - children: [new Paragraph({ children: [new TextRun("Regular data")] })] - }), - new TableCell({ - borders: cellBorders, - width: { size: 4680, type: WidthType.DXA }, // ALSO set width on each cell - children: [ - new Paragraph({ - numbering: { reference: "bullet-list", level: 0 }, - children: [new TextRun("First bullet point")] - }), - new Paragraph({ - numbering: { reference: "bullet-list", level: 0 }, - children: [new TextRun("Second bullet point")] - }) - ] - }) - ] - }) - ] -}) -``` - -**IMPORTANT: Table Width & Borders** -- Use BOTH `columnWidths: [width1, width2, ...]` array AND `width: { size: X, type: WidthType.DXA }` on each cell -- Values in DXA (twentieths of a point): 1440 = 1 inch, Letter usable width = 9360 DXA (with 1" margins) -- Apply borders to individual `TableCell` elements, NOT the `Table` itself - -**Precomputed Column Widths (Letter size with 1" margins = 9360 DXA total):** -- **2 columns:** `columnWidths: [4680, 4680]` (equal width) -- **3 columns:** `columnWidths: [3120, 3120, 3120]` (equal width) - -## Links & Navigation -```javascript -// TOC (requires headings) - CRITICAL: Use HeadingLevel only, NOT custom styles -// ❌ WRONG: new Paragraph({ heading: HeadingLevel.HEADING_1, style: "customHeader", children: [new TextRun("Title")] }) -// ✅ CORRECT: new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Title")] }) -new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" }), - -// External link -new Paragraph({ - children: [new ExternalHyperlink({ - children: [new TextRun({ text: "Google", style: "Hyperlink" })], - link: "https://www.google.com" - })] -}), - -// Internal link & bookmark -new Paragraph({ - children: [new InternalHyperlink({ - children: [new TextRun({ text: "Go to Section", style: "Hyperlink" })], - anchor: "section1" - })] -}), -new Paragraph({ - children: [new TextRun("Section Content")], - bookmark: { id: "section1", name: "section1" } -}), -``` - -## Images & Media -```javascript -// Basic image with sizing & positioning -// CRITICAL: Always specify 'type' parameter - it's REQUIRED for ImageRun -new Paragraph({ - alignment: AlignmentType.CENTER, - children: [new ImageRun({ - type: "png", // NEW REQUIREMENT: Must specify image type (png, jpg, jpeg, gif, bmp, svg) - data: fs.readFileSync("image.png"), - transformation: { width: 200, height: 150, rotation: 0 }, // rotation in degrees - altText: { title: "Logo", description: "Company logo", name: "Name" } // IMPORTANT: All three fields are required - })] -}) -``` - -## Page Breaks -```javascript -// Manual page break -new Paragraph({ children: [new PageBreak()] }), - -// Page break before paragraph -new Paragraph({ - pageBreakBefore: true, - children: [new TextRun("This starts on a new page")] -}) - -// ⚠️ CRITICAL: NEVER use PageBreak standalone - it will create invalid XML that Word cannot open -// ❌ WRONG: new PageBreak() -// ✅ CORRECT: new Paragraph({ children: [new PageBreak()] }) -``` - -## Headers/Footers & Page Setup -```javascript -const doc = new Document({ - sections: [{ - properties: { - page: { - margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }, // 1440 = 1 inch - size: { orientation: PageOrientation.LANDSCAPE }, - pageNumbers: { start: 1, formatType: "decimal" } // "upperRoman", "lowerRoman", "upperLetter", "lowerLetter" - } - }, - headers: { - default: new Header({ children: [new Paragraph({ - alignment: AlignmentType.RIGHT, - children: [new TextRun("Header Text")] - })] }) - }, - footers: { - default: new Footer({ children: [new Paragraph({ - alignment: AlignmentType.CENTER, - children: [new TextRun("Page "), new TextRun({ children: [PageNumber.CURRENT] }), new TextRun(" of "), new TextRun({ children: [PageNumber.TOTAL_PAGES] })] - })] }) - }, - children: [/* content */] - }] -}); -``` - -## Tabs -```javascript -new Paragraph({ - tabStops: [ - { type: TabStopType.LEFT, position: TabStopPosition.MAX / 4 }, - { type: TabStopType.CENTER, position: TabStopPosition.MAX / 2 }, - { type: TabStopType.RIGHT, position: TabStopPosition.MAX * 3 / 4 } - ], - children: [new TextRun("Left\tCenter\tRight")] -}) -``` - -## Constants & Quick Reference -- **Underlines:** `SINGLE`, `DOUBLE`, `WAVY`, `DASH` -- **Borders:** `SINGLE`, `DOUBLE`, `DASHED`, `DOTTED` -- **Numbering:** `DECIMAL` (1,2,3), `UPPER_ROMAN` (I,II,III), `LOWER_LETTER` (a,b,c) -- **Tabs:** `LEFT`, `CENTER`, `RIGHT`, `DECIMAL` -- **Symbols:** `"2022"` (•), `"00A9"` (©), `"00AE"` (®), `"2122"` (™), `"00B0"` (°), `"F070"` (✓), `"F0FC"` (✗) - -## Critical Issues & Common Mistakes -- **CRITICAL: PageBreak must ALWAYS be inside a Paragraph** - standalone PageBreak creates invalid XML that Word cannot open -- **ALWAYS use ShadingType.CLEAR for table cell shading** - Never use ShadingType.SOLID (causes black background). -- Measurements in DXA (1440 = 1 inch) | Each table cell needs ≥1 Paragraph | TOC requires HeadingLevel styles only -- **ALWAYS use custom styles** with Arial font for professional appearance and proper visual hierarchy -- **ALWAYS set a default font** using `styles.default.document.run.font` - Arial recommended -- **ALWAYS use columnWidths array for tables** + individual cell widths for compatibility -- **NEVER use unicode symbols for bullets** - always use proper numbering configuration with `LevelFormat.BULLET` constant (NOT the string "bullet") -- **NEVER use \n for line breaks anywhere** - always use separate Paragraph elements for each line -- **ALWAYS use TextRun objects within Paragraph children** - never use text property directly on Paragraph -- **CRITICAL for images**: ImageRun REQUIRES `type` parameter - always specify "png", "jpg", "jpeg", "gif", "bmp", or "svg" -- **CRITICAL for bullets**: Must use `LevelFormat.BULLET` constant, not string "bullet", and include `text: "•"` for the bullet character -- **CRITICAL for numbering**: Each numbering reference creates an INDEPENDENT list. Same reference = continues numbering (1,2,3 then 4,5,6). Different reference = restarts at 1 (1,2,3 then 1,2,3). Use unique reference names for each separate numbered section! -- **CRITICAL for TOC**: When using TableOfContents, headings must use HeadingLevel ONLY - do NOT add custom styles to heading paragraphs or TOC will break -- **Tables**: Set `columnWidths` array + individual cell widths, apply borders to cells not table -- **Set table margins at TABLE level** for consistent cell padding (avoids repetition per cell) \ No newline at end of file diff --git a/document-skills/docx/ooxml.md b/document-skills/docx/ooxml.md deleted file mode 100644 index 7677e7b83..000000000 --- a/document-skills/docx/ooxml.md +++ /dev/null @@ -1,610 +0,0 @@ -# Office Open XML Technical Reference - -**Important: Read this entire document before starting.** This document covers: -- [Technical Guidelines](#technical-guidelines) - Schema compliance rules and validation requirements -- [Document Content Patterns](#document-content-patterns) - XML patterns for headings, lists, tables, formatting, etc. -- [Document Library (Python)](#document-library-python) - Recommended approach for OOXML manipulation with automatic infrastructure setup -- [Tracked Changes (Redlining)](#tracked-changes-redlining) - XML patterns for implementing tracked changes - -## Technical Guidelines - -### Schema Compliance -- **Element ordering in ``**: ``, ``, ``, ``, `` -- **Whitespace**: Add `xml:space='preserve'` to `` elements with leading/trailing spaces -- **Unicode**: Escape characters in ASCII content: `"` becomes `“` - - **Character encoding reference**: Curly quotes `""` become `“”`, apostrophe `'` becomes `’`, em-dash `—` becomes `—` -- **Tracked changes**: Use `` and `` tags with `w:author="Claude"` outside `` elements - - **Critical**: `` closes with ``, `` closes with `` - never mix - - **RSIDs must be 8-digit hex**: Use values like `00AB1234` (only 0-9, A-F characters) - - **trackRevisions placement**: Add `` after `` in settings.xml -- **Images**: Add to `word/media/`, reference in `document.xml`, set dimensions to prevent overflow - -## Document Content Patterns - -### Basic Structure -```xml - - Text content - -``` - -### Headings and Styles -```xml - - - - - - Document Title - - - - - Section Heading - -``` - -### Text Formatting -```xml - -Bold - -Italic - -Underlined - -Highlighted -``` - -### Lists -```xml - - - - - - - - First item - - - - - - - - - - New list item 1 - - - - - - - - - - - Bullet item - -``` - -### Tables -```xml - - - - - - - - - - - - Cell 1 - - - - Cell 2 - - - -``` - -### Layout -```xml - - - - - - - - - - - - New Section Title - - - - - - - - - - Centered text - - - - - - - - Monospace text - - - - - - - This text is Courier New - - and this text uses default font - -``` - -## File Updates - -When adding content, update these files: - -**`word/_rels/document.xml.rels`:** -```xml - - -``` - -**`[Content_Types].xml`:** -```xml - - -``` - -### Images -**CRITICAL**: Calculate dimensions to prevent page overflow and maintain aspect ratio. - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### Links (Hyperlinks) - -**IMPORTANT**: All hyperlinks (both internal and external) require the Hyperlink style to be defined in styles.xml. Without this style, links will look like regular text instead of blue underlined clickable links. - -**External Links:** -```xml - - - - - Link Text - - - - - -``` - -**Internal Links:** - -```xml - - - - - Link Text - - - - - -Target content - -``` - -**Hyperlink Style (required in styles.xml):** -```xml - - - - - - - - - - -``` - -## Document Library (Python) - -Use the Document class from `scripts/document.py` for all tracked changes and comments. It automatically handles infrastructure setup (people.xml, RSIDs, settings.xml, comment files, relationships, content types). Only use direct XML manipulation for complex scenarios not supported by the library. - -**Working with Unicode and Entities:** -- **Searching**: Both entity notation and Unicode characters work - `contains="“Company"` and `contains="\u201cCompany"` find the same text -- **Replacing**: Use either entities (`“`) or Unicode (`\u201c`) - both work and will be converted appropriately based on the file's encoding (ascii → entities, utf-8 → Unicode) - -### Initialization - -**Find the docx skill root** (directory containing `scripts/` and `ooxml/`): -```bash -# Search for document.py to locate the skill root -# Note: /mnt/skills is used here as an example; check your context for the actual location -find /mnt/skills -name "document.py" -path "*/docx/scripts/*" 2>/dev/null | head -1 -# Example output: /mnt/skills/docx/scripts/document.py -# Skill root is: /mnt/skills/docx -``` - -**Run your script with PYTHONPATH** set to the docx skill root: -```bash -PYTHONPATH=/mnt/skills/docx python your_script.py -``` - -**In your script**, import from the skill root: -```python -from scripts.document import Document, DocxXMLEditor - -# Basic initialization (automatically creates temp copy and sets up infrastructure) -doc = Document('unpacked') - -# Customize author and initials -doc = Document('unpacked', author="John Doe", initials="JD") - -# Enable track revisions mode -doc = Document('unpacked', track_revisions=True) - -# Specify custom RSID (auto-generated if not provided) -doc = Document('unpacked', rsid="07DC5ECB") -``` - -### Creating Tracked Changes - -**CRITICAL**: Only mark text that actually changes. Keep ALL unchanged text outside ``/`` tags. Marking unchanged text makes edits unprofessional and harder to review. - -**Attribute Handling**: The Document class auto-injects attributes (w:id, w:date, w:rsidR, w:rsidDel, w16du:dateUtc, xml:space) into new elements. When preserving unchanged text from the original document, copy the original `` element with its existing attributes to maintain document integrity. - -**Method Selection Guide**: -- **Adding your own changes to regular text**: Use `replace_node()` with ``/`` tags, or `suggest_deletion()` for removing entire `` or `` elements -- **Partially modifying another author's tracked change**: Use `replace_node()` to nest your changes inside their ``/`` -- **Completely rejecting another author's insertion**: Use `revert_insertion()` on the `` element (NOT `suggest_deletion()`) -- **Completely rejecting another author's deletion**: Use `revert_deletion()` on the `` element to restore deleted content using tracked changes - -```python -# Minimal edit - change one word: "The report is monthly" → "The report is quarterly" -# Original: The report is monthly -node = doc["word/document.xml"].get_node(tag="w:r", contains="The report is monthly") -rpr = tags[0].toxml() if (tags := node.getElementsByTagName("w:rPr")) else "" -replacement = f'{rpr}The report is {rpr}monthly{rpr}quarterly' -doc["word/document.xml"].replace_node(node, replacement) - -# Minimal edit - change number: "within 30 days" → "within 45 days" -# Original: within 30 days -node = doc["word/document.xml"].get_node(tag="w:r", contains="within 30 days") -rpr = tags[0].toxml() if (tags := node.getElementsByTagName("w:rPr")) else "" -replacement = f'{rpr}within {rpr}30{rpr}45{rpr} days' -doc["word/document.xml"].replace_node(node, replacement) - -# Complete replacement - preserve formatting even when replacing all text -node = doc["word/document.xml"].get_node(tag="w:r", contains="apple") -rpr = tags[0].toxml() if (tags := node.getElementsByTagName("w:rPr")) else "" -replacement = f'{rpr}apple{rpr}banana orange' -doc["word/document.xml"].replace_node(node, replacement) - -# Insert new content (no attributes needed - auto-injected) -node = doc["word/document.xml"].get_node(tag="w:r", contains="existing text") -doc["word/document.xml"].insert_after(node, 'new text') - -# Partially delete another author's insertion -# Original: quarterly financial report -# Goal: Delete only "financial" to make it "quarterly report" -node = doc["word/document.xml"].get_node(tag="w:ins", attrs={"w:id": "5"}) -# IMPORTANT: Preserve w:author="Jane Smith" on the outer to maintain authorship -replacement = ''' - quarterly - financial - report -''' -doc["word/document.xml"].replace_node(node, replacement) - -# Change part of another author's insertion -# Original: in silence, safe and sound -# Goal: Change "safe and sound" to "soft and unbound" -node = doc["word/document.xml"].get_node(tag="w:ins", attrs={"w:id": "8"}) -replacement = f''' - in silence, - - - soft and unbound - - - safe and sound -''' -doc["word/document.xml"].replace_node(node, replacement) - -# Delete entire run (use only when deleting all content; use replace_node for partial deletions) -node = doc["word/document.xml"].get_node(tag="w:r", contains="text to delete") -doc["word/document.xml"].suggest_deletion(node) - -# Delete entire paragraph (in-place, handles both regular and numbered list paragraphs) -para = doc["word/document.xml"].get_node(tag="w:p", contains="paragraph to delete") -doc["word/document.xml"].suggest_deletion(para) - -# Add new numbered list item -target_para = doc["word/document.xml"].get_node(tag="w:p", contains="existing list item") -pPr = tags[0].toxml() if (tags := target_para.getElementsByTagName("w:pPr")) else "" -new_item = f'{pPr}New item' -tracked_para = DocxXMLEditor.suggest_paragraph(new_item) -doc["word/document.xml"].insert_after(target_para, tracked_para) -# Optional: add spacing paragraph before content for better visual separation -# spacing = DocxXMLEditor.suggest_paragraph('') -# doc["word/document.xml"].insert_after(target_para, spacing + tracked_para) -``` - -### Adding Comments - -```python -# Add comment spanning two existing tracked changes -# Note: w:id is auto-generated. Only search by w:id if you know it from XML inspection -start_node = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "1"}) -end_node = doc["word/document.xml"].get_node(tag="w:ins", attrs={"w:id": "2"}) -doc.add_comment(start=start_node, end=end_node, text="Explanation of this change") - -# Add comment on a paragraph -para = doc["word/document.xml"].get_node(tag="w:p", contains="paragraph text") -doc.add_comment(start=para, end=para, text="Comment on this paragraph") - -# Add comment on newly created tracked change -# First create the tracked change -node = doc["word/document.xml"].get_node(tag="w:r", contains="old") -new_nodes = doc["word/document.xml"].replace_node( - node, - 'oldnew' -) -# Then add comment on the newly created elements -# new_nodes[0] is the , new_nodes[1] is the -doc.add_comment(start=new_nodes[0], end=new_nodes[1], text="Changed old to new per requirements") - -# Reply to existing comment -doc.reply_to_comment(parent_comment_id=0, text="I agree with this change") -``` - -### Rejecting Tracked Changes - -**IMPORTANT**: Use `revert_insertion()` to reject insertions and `revert_deletion()` to restore deletions using tracked changes. Use `suggest_deletion()` only for regular unmarked content. - -```python -# Reject insertion (wraps it in deletion) -# Use this when another author inserted text that you want to delete -ins = doc["word/document.xml"].get_node(tag="w:ins", attrs={"w:id": "5"}) -nodes = doc["word/document.xml"].revert_insertion(ins) # Returns [ins] - -# Reject deletion (creates insertion to restore deleted content) -# Use this when another author deleted text that you want to restore -del_elem = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "3"}) -nodes = doc["word/document.xml"].revert_deletion(del_elem) # Returns [del_elem, new_ins] - -# Reject all insertions in a paragraph -para = doc["word/document.xml"].get_node(tag="w:p", contains="paragraph text") -nodes = doc["word/document.xml"].revert_insertion(para) # Returns [para] - -# Reject all deletions in a paragraph -para = doc["word/document.xml"].get_node(tag="w:p", contains="paragraph text") -nodes = doc["word/document.xml"].revert_deletion(para) # Returns [para] -``` - -### Inserting Images - -**CRITICAL**: The Document class works with a temporary copy at `doc.unpacked_path`. Always copy images to this temp directory, not the original unpacked folder. - -```python -from PIL import Image -import shutil, os - -# Initialize document first -doc = Document('unpacked') - -# Copy image and calculate full-width dimensions with aspect ratio -media_dir = os.path.join(doc.unpacked_path, 'word/media') -os.makedirs(media_dir, exist_ok=True) -shutil.copy('image.png', os.path.join(media_dir, 'image1.png')) -img = Image.open(os.path.join(media_dir, 'image1.png')) -width_emus = int(6.5 * 914400) # 6.5" usable width, 914400 EMUs/inch -height_emus = int(width_emus * img.size[1] / img.size[0]) - -# Add relationship and content type -rels_editor = doc['word/_rels/document.xml.rels'] -next_rid = rels_editor.get_next_rid() -rels_editor.append_to(rels_editor.dom.documentElement, - f'') -doc['[Content_Types].xml'].append_to(doc['[Content_Types].xml'].dom.documentElement, - '') - -# Insert image -node = doc["word/document.xml"].get_node(tag="w:p", line_number=100) -doc["word/document.xml"].insert_after(node, f''' - - - - - - - - - - - - - - - - - -''') -``` - -### Getting Nodes - -```python -# By text content -node = doc["word/document.xml"].get_node(tag="w:p", contains="specific text") - -# By line range -para = doc["word/document.xml"].get_node(tag="w:p", line_number=range(100, 150)) - -# By attributes -node = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "1"}) - -# By exact line number (must be line number where tag opens) -para = doc["word/document.xml"].get_node(tag="w:p", line_number=42) - -# Combine filters -node = doc["word/document.xml"].get_node(tag="w:r", line_number=range(40, 60), contains="text") - -# Disambiguate when text appears multiple times - add line_number range -node = doc["word/document.xml"].get_node(tag="w:r", contains="Section", line_number=range(2400, 2500)) -``` - -### Saving - -```python -# Save with automatic validation (copies back to original directory) -doc.save() # Validates by default, raises error if validation fails - -# Save to different location -doc.save('modified-unpacked') - -# Skip validation (debugging only - needing this in production indicates XML issues) -doc.save(validate=False) -``` - -### Direct DOM Manipulation - -For complex scenarios not covered by the library: - -```python -# Access any XML file -editor = doc["word/document.xml"] -editor = doc["word/comments.xml"] - -# Direct DOM access (defusedxml.minidom.Document) -node = doc["word/document.xml"].get_node(tag="w:p", line_number=5) -parent = node.parentNode -parent.removeChild(node) -parent.appendChild(node) # Move to end - -# General document manipulation (without tracked changes) -old_node = doc["word/document.xml"].get_node(tag="w:p", contains="original text") -doc["word/document.xml"].replace_node(old_node, "replacement text") - -# Multiple insertions - use return value to maintain order -node = doc["word/document.xml"].get_node(tag="w:r", line_number=100) -nodes = doc["word/document.xml"].insert_after(node, "A") -nodes = doc["word/document.xml"].insert_after(nodes[-1], "B") -nodes = doc["word/document.xml"].insert_after(nodes[-1], "C") -# Results in: original_node, A, B, C -``` - -## Tracked Changes (Redlining) - -**Use the Document class above for all tracked changes.** The patterns below are for reference when constructing replacement XML strings. - -### Validation Rules -The validator checks that the document text matches the original after reverting Claude's changes. This means: -- **NEVER modify text inside another author's `` or `` tags** -- **ALWAYS use nested deletions** to remove another author's insertions -- **Every edit must be properly tracked** with `` or `` tags - -### Tracked Change Patterns - -**CRITICAL RULES**: -1. Never modify the content inside another author's tracked changes. Always use nested deletions. -2. **XML Structure**: Always place `` and `` at paragraph level containing complete `` elements. Never nest inside `` elements - this creates invalid XML that breaks document processing. - -**Text Insertion:** -```xml - - - inserted text - - -``` - -**Text Deletion:** -```xml - - - deleted text - - -``` - -**Deleting Another Author's Insertion (MUST use nested structure):** -```xml - - - - monthly - - - - weekly - -``` - -**Restoring Another Author's Deletion:** -```xml - - - within 30 days - - - within 30 days - -``` \ No newline at end of file diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd deleted file mode 100644 index 6454ef9a9..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +++ /dev/null @@ -1,1499 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd deleted file mode 100644 index afa4f463e..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd deleted file mode 100644 index 64e66b8ab..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +++ /dev/null @@ -1,1085 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd deleted file mode 100644 index 687eea829..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd deleted file mode 100644 index 6ac81b06b..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +++ /dev/null @@ -1,3081 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd deleted file mode 100644 index 1dbf05140..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd deleted file mode 100644 index f1af17db4..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd deleted file mode 100644 index 0a185ab6e..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd deleted file mode 100644 index 14ef48886..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +++ /dev/null @@ -1,1676 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd deleted file mode 100644 index c20f3bf14..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd deleted file mode 100644 index ac6025226..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd deleted file mode 100644 index 424b8ba8d..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +++ /dev/null @@ -1,174 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd deleted file mode 100644 index 2bddce292..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd deleted file mode 100644 index 8a8c18ba2..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd deleted file mode 100644 index 5c42706a0..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd deleted file mode 100644 index 853c341c8..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd deleted file mode 100644 index da835ee82..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd deleted file mode 100644 index 87ad2658f..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +++ /dev/null @@ -1,582 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd deleted file mode 100644 index 9e86f1b2b..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd deleted file mode 100644 index d0be42e75..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +++ /dev/null @@ -1,4439 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd deleted file mode 100644 index 8821dd183..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +++ /dev/null @@ -1,570 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd deleted file mode 100644 index ca2575c75..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd deleted file mode 100644 index dd079e603..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd deleted file mode 100644 index 3dd6cf625..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd deleted file mode 100644 index f1041e34e..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd deleted file mode 100644 index 9c5b7a633..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +++ /dev/null @@ -1,3646 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd b/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd deleted file mode 100644 index 0f13678d8..000000000 --- a/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - See http://www.w3.org/XML/1998/namespace.html and - http://www.w3.org/TR/REC-xml for information about this namespace. - - This schema document describes the XML namespace, in a form - suitable for import by other schema documents. - - Note that local names in this namespace are intended to be defined - only by the World Wide Web Consortium or its subgroups. The - following names are currently defined in this namespace and should - not be used with conflicting semantics by any Working Group, - specification, or document instance: - - base (as an attribute name): denotes an attribute whose value - provides a URI to be used as the base for interpreting any - relative URIs in the scope of the element on which it - appears; its value is inherited. This name is reserved - by virtue of its definition in the XML Base specification. - - lang (as an attribute name): denotes an attribute whose value - is a language code for the natural language of the content of - any element; its value is inherited. This name is reserved - by virtue of its definition in the XML specification. - - space (as an attribute name): denotes an attribute whose - value is a keyword indicating what whitespace processing - discipline is intended for the content of the element; its - value is inherited. This name is reserved by virtue of its - definition in the XML specification. - - Father (in any context at all): denotes Jon Bosak, the chair of - the original XML Working Group. This name is reserved by - the following decision of the W3C XML Plenary and - XML Coordination groups: - - In appreciation for his vision, leadership and dedication - the W3C XML Plenary on this 10th day of February, 2000 - reserves for Jon Bosak in perpetuity the XML name - xml:Father - - - - - This schema defines attributes and an attribute group - suitable for use by - schemas wishing to allow xml:base, xml:lang or xml:space attributes - on elements they define. - - To enable this, such a schema must import this schema - for the XML namespace, e.g. as follows: - <schema . . .> - . . . - <import namespace="http://www.w3.org/XML/1998/namespace" - schemaLocation="http://www.w3.org/2001/03/xml.xsd"/> - - Subsequently, qualified reference to any of the attributes - or the group defined below will have the desired effect, e.g. - - <type . . .> - . . . - <attributeGroup ref="xml:specialAttrs"/> - - will define a type which will schema-validate an instance - element with any of those attributes - - - - In keeping with the XML Schema WG's standard versioning - policy, this schema document will persist at - http://www.w3.org/2001/03/xml.xsd. - At the date of issue it can also be found at - http://www.w3.org/2001/xml.xsd. - The schema document at that URI may however change in the future, - in order to remain compatible with the latest version of XML Schema - itself. In other words, if the XML Schema namespace changes, the version - of this document at - http://www.w3.org/2001/xml.xsd will change - accordingly; the version at - http://www.w3.org/2001/03/xml.xsd will not change. - - - - - - In due course, we should install the relevant ISO 2- and 3-letter - codes as the enumerated possible values . . . - - - - - - - - - - - - - - - See http://www.w3.org/TR/xmlbase/ for - information about this attribute. - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd b/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd deleted file mode 100644 index a6de9d273..000000000 --- a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd b/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd deleted file mode 100644 index 10e978b66..000000000 --- a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd b/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd deleted file mode 100644 index 4248bf7a3..000000000 --- a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd b/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd deleted file mode 100644 index 564974671..000000000 --- a/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/mce/mc.xsd b/document-skills/docx/ooxml/schemas/mce/mc.xsd deleted file mode 100644 index ef725457c..000000000 --- a/document-skills/docx/ooxml/schemas/mce/mc.xsd +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-2010.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-2010.xsd deleted file mode 100644 index f65f77773..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-2012.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-2012.xsd deleted file mode 100644 index 6b00755a9..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-2018.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-2018.xsd deleted file mode 100644 index f321d333a..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd deleted file mode 100644 index 364c6a9b8..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd deleted file mode 100644 index fed9d15b7..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd deleted file mode 100644 index 680cf1540..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/document-skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd b/document-skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd deleted file mode 100644 index 89ada9083..000000000 --- a/document-skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/document-skills/docx/ooxml/scripts/pack.py b/document-skills/docx/ooxml/scripts/pack.py deleted file mode 100755 index 68bc0886f..000000000 --- a/document-skills/docx/ooxml/scripts/pack.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -""" -Tool to pack a directory into a .docx, .pptx, or .xlsx file with XML formatting undone. - -Example usage: - python pack.py [--force] -""" - -import argparse -import shutil -import subprocess -import sys -import tempfile -import defusedxml.minidom -import zipfile -from pathlib import Path - - -def main(): - parser = argparse.ArgumentParser(description="Pack a directory into an Office file") - parser.add_argument("input_directory", help="Unpacked Office document directory") - parser.add_argument("output_file", help="Output Office file (.docx/.pptx/.xlsx)") - parser.add_argument("--force", action="store_true", help="Skip validation") - args = parser.parse_args() - - try: - success = pack_document( - args.input_directory, args.output_file, validate=not args.force - ) - - # Show warning if validation was skipped - if args.force: - print("Warning: Skipped validation, file may be corrupt", file=sys.stderr) - # Exit with error if validation failed - elif not success: - print("Contents would produce a corrupt file.", file=sys.stderr) - print("Please validate XML before repacking.", file=sys.stderr) - print("Use --force to skip validation and pack anyway.", file=sys.stderr) - sys.exit(1) - - except ValueError as e: - sys.exit(f"Error: {e}") - - -def pack_document(input_dir, output_file, validate=False): - """Pack a directory into an Office file (.docx/.pptx/.xlsx). - - Args: - input_dir: Path to unpacked Office document directory - output_file: Path to output Office file - validate: If True, validates with soffice (default: False) - - Returns: - bool: True if successful, False if validation failed - """ - input_dir = Path(input_dir) - output_file = Path(output_file) - - if not input_dir.is_dir(): - raise ValueError(f"{input_dir} is not a directory") - if output_file.suffix.lower() not in {".docx", ".pptx", ".xlsx"}: - raise ValueError(f"{output_file} must be a .docx, .pptx, or .xlsx file") - - # Work in temporary directory to avoid modifying original - with tempfile.TemporaryDirectory() as temp_dir: - temp_content_dir = Path(temp_dir) / "content" - shutil.copytree(input_dir, temp_content_dir) - - # Process XML files to remove pretty-printing whitespace - for pattern in ["*.xml", "*.rels"]: - for xml_file in temp_content_dir.rglob(pattern): - condense_xml(xml_file) - - # Create final Office file as zip archive - output_file.parent.mkdir(parents=True, exist_ok=True) - with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf: - for f in temp_content_dir.rglob("*"): - if f.is_file(): - zf.write(f, f.relative_to(temp_content_dir)) - - # Validate if requested - if validate: - if not validate_document(output_file): - output_file.unlink() # Delete the corrupt file - return False - - return True - - -def validate_document(doc_path): - """Validate document by converting to HTML with soffice.""" - # Determine the correct filter based on file extension - match doc_path.suffix.lower(): - case ".docx": - filter_name = "html:HTML" - case ".pptx": - filter_name = "html:impress_html_Export" - case ".xlsx": - filter_name = "html:HTML (StarCalc)" - - with tempfile.TemporaryDirectory() as temp_dir: - try: - result = subprocess.run( - [ - "soffice", - "--headless", - "--convert-to", - filter_name, - "--outdir", - temp_dir, - str(doc_path), - ], - capture_output=True, - timeout=10, - text=True, - ) - if not (Path(temp_dir) / f"{doc_path.stem}.html").exists(): - error_msg = result.stderr.strip() or "Document validation failed" - print(f"Validation error: {error_msg}", file=sys.stderr) - return False - return True - except FileNotFoundError: - print("Warning: soffice not found. Skipping validation.", file=sys.stderr) - return True - except subprocess.TimeoutExpired: - print("Validation error: Timeout during conversion", file=sys.stderr) - return False - except Exception as e: - print(f"Validation error: {e}", file=sys.stderr) - return False - - -def condense_xml(xml_file): - """Strip unnecessary whitespace and remove comments.""" - with open(xml_file, "r", encoding="utf-8") as f: - dom = defusedxml.minidom.parse(f) - - # Process each element to remove whitespace and comments - for element in dom.getElementsByTagName("*"): - # Skip w:t elements and their processing - if element.tagName.endswith(":t"): - continue - - # Remove whitespace-only text nodes and comment nodes - for child in list(element.childNodes): - if ( - child.nodeType == child.TEXT_NODE - and child.nodeValue - and child.nodeValue.strip() == "" - ) or child.nodeType == child.COMMENT_NODE: - element.removeChild(child) - - # Write back the condensed XML - with open(xml_file, "wb") as f: - f.write(dom.toxml(encoding="UTF-8")) - - -if __name__ == "__main__": - main() diff --git a/document-skills/docx/ooxml/scripts/unpack.py b/document-skills/docx/ooxml/scripts/unpack.py deleted file mode 100755 index 493879881..000000000 --- a/document-skills/docx/ooxml/scripts/unpack.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -"""Unpack and format XML contents of Office files (.docx, .pptx, .xlsx)""" - -import random -import sys -import defusedxml.minidom -import zipfile -from pathlib import Path - -# Get command line arguments -assert len(sys.argv) == 3, "Usage: python unpack.py " -input_file, output_dir = sys.argv[1], sys.argv[2] - -# Extract and format -output_path = Path(output_dir) -output_path.mkdir(parents=True, exist_ok=True) -zipfile.ZipFile(input_file).extractall(output_path) - -# Pretty print all XML files -xml_files = list(output_path.rglob("*.xml")) + list(output_path.rglob("*.rels")) -for xml_file in xml_files: - content = xml_file.read_text(encoding="utf-8") - dom = defusedxml.minidom.parseString(content) - xml_file.write_bytes(dom.toprettyxml(indent=" ", encoding="ascii")) - -# For .docx files, suggest an RSID for tracked changes -if input_file.endswith(".docx"): - suggested_rsid = "".join(random.choices("0123456789ABCDEF", k=8)) - print(f"Suggested RSID for edit session: {suggested_rsid}") diff --git a/document-skills/docx/ooxml/scripts/validate.py b/document-skills/docx/ooxml/scripts/validate.py deleted file mode 100755 index 508c5891f..000000000 --- a/document-skills/docx/ooxml/scripts/validate.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -""" -Command line tool to validate Office document XML files against XSD schemas and tracked changes. - -Usage: - python validate.py --original -""" - -import argparse -import sys -from pathlib import Path - -from validation import DOCXSchemaValidator, PPTXSchemaValidator, RedliningValidator - - -def main(): - parser = argparse.ArgumentParser(description="Validate Office document XML files") - parser.add_argument( - "unpacked_dir", - help="Path to unpacked Office document directory", - ) - parser.add_argument( - "--original", - required=True, - help="Path to original file (.docx/.pptx/.xlsx)", - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - help="Enable verbose output", - ) - args = parser.parse_args() - - # Validate paths - unpacked_dir = Path(args.unpacked_dir) - original_file = Path(args.original) - file_extension = original_file.suffix.lower() - assert unpacked_dir.is_dir(), f"Error: {unpacked_dir} is not a directory" - assert original_file.is_file(), f"Error: {original_file} is not a file" - assert file_extension in [".docx", ".pptx", ".xlsx"], ( - f"Error: {original_file} must be a .docx, .pptx, or .xlsx file" - ) - - # Run validations - match file_extension: - case ".docx": - validators = [DOCXSchemaValidator, RedliningValidator] - case ".pptx": - validators = [PPTXSchemaValidator] - case _: - print(f"Error: Validation not supported for file type {file_extension}") - sys.exit(1) - - # Run validators - success = True - for V in validators: - validator = V(unpacked_dir, original_file, verbose=args.verbose) - if not validator.validate(): - success = False - - if success: - print("All validations PASSED!") - - sys.exit(0 if success else 1) - - -if __name__ == "__main__": - main() diff --git a/document-skills/docx/ooxml/scripts/validation/__init__.py b/document-skills/docx/ooxml/scripts/validation/__init__.py deleted file mode 100644 index db092ece7..000000000 --- a/document-skills/docx/ooxml/scripts/validation/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Validation modules for Word document processing. -""" - -from .base import BaseSchemaValidator -from .docx import DOCXSchemaValidator -from .pptx import PPTXSchemaValidator -from .redlining import RedliningValidator - -__all__ = [ - "BaseSchemaValidator", - "DOCXSchemaValidator", - "PPTXSchemaValidator", - "RedliningValidator", -] diff --git a/document-skills/docx/ooxml/scripts/validation/base.py b/document-skills/docx/ooxml/scripts/validation/base.py deleted file mode 100644 index 0681b199c..000000000 --- a/document-skills/docx/ooxml/scripts/validation/base.py +++ /dev/null @@ -1,951 +0,0 @@ -""" -Base validator with common validation logic for document files. -""" - -import re -from pathlib import Path - -import lxml.etree - - -class BaseSchemaValidator: - """Base validator with common validation logic for document files.""" - - # Elements whose 'id' attributes must be unique within their file - # Format: element_name -> (attribute_name, scope) - # scope can be 'file' (unique within file) or 'global' (unique across all files) - UNIQUE_ID_REQUIREMENTS = { - # Word elements - "comment": ("id", "file"), # Comment IDs in comments.xml - "commentrangestart": ("id", "file"), # Must match comment IDs - "commentrangeend": ("id", "file"), # Must match comment IDs - "bookmarkstart": ("id", "file"), # Bookmark start IDs - "bookmarkend": ("id", "file"), # Bookmark end IDs - # Note: ins and del (track changes) can share IDs when part of same revision - # PowerPoint elements - "sldid": ("id", "file"), # Slide IDs in presentation.xml - "sldmasterid": ("id", "global"), # Slide master IDs must be globally unique - "sldlayoutid": ("id", "global"), # Slide layout IDs must be globally unique - "cm": ("authorid", "file"), # Comment author IDs - # Excel elements - "sheet": ("sheetid", "file"), # Sheet IDs in workbook.xml - "definedname": ("id", "file"), # Named range IDs - # Drawing/Shape elements (all formats) - "cxnsp": ("id", "file"), # Connection shape IDs - "sp": ("id", "file"), # Shape IDs - "pic": ("id", "file"), # Picture IDs - "grpsp": ("id", "file"), # Group shape IDs - } - - # Mapping of element names to expected relationship types - # Subclasses should override this with format-specific mappings - ELEMENT_RELATIONSHIP_TYPES = {} - - # Unified schema mappings for all Office document types - SCHEMA_MAPPINGS = { - # Document type specific schemas - "word": "ISO-IEC29500-4_2016/wml.xsd", # Word documents - "ppt": "ISO-IEC29500-4_2016/pml.xsd", # PowerPoint presentations - "xl": "ISO-IEC29500-4_2016/sml.xsd", # Excel spreadsheets - # Common file types - "[Content_Types].xml": "ecma/fouth-edition/opc-contentTypes.xsd", - "app.xml": "ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd", - "core.xml": "ecma/fouth-edition/opc-coreProperties.xsd", - "custom.xml": "ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd", - ".rels": "ecma/fouth-edition/opc-relationships.xsd", - # Word-specific files - "people.xml": "microsoft/wml-2012.xsd", - "commentsIds.xml": "microsoft/wml-cid-2016.xsd", - "commentsExtensible.xml": "microsoft/wml-cex-2018.xsd", - "commentsExtended.xml": "microsoft/wml-2012.xsd", - # Chart files (common across document types) - "chart": "ISO-IEC29500-4_2016/dml-chart.xsd", - # Theme files (common across document types) - "theme": "ISO-IEC29500-4_2016/dml-main.xsd", - # Drawing and media files - "drawing": "ISO-IEC29500-4_2016/dml-main.xsd", - } - - # Unified namespace constants - MC_NAMESPACE = "http://schemas.openxmlformats.org/markup-compatibility/2006" - XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace" - - # Common OOXML namespaces used across validators - PACKAGE_RELATIONSHIPS_NAMESPACE = ( - "http://schemas.openxmlformats.org/package/2006/relationships" - ) - OFFICE_RELATIONSHIPS_NAMESPACE = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships" - ) - CONTENT_TYPES_NAMESPACE = ( - "http://schemas.openxmlformats.org/package/2006/content-types" - ) - - # Folders where we should clean ignorable namespaces - MAIN_CONTENT_FOLDERS = {"word", "ppt", "xl"} - - # All allowed OOXML namespaces (superset of all document types) - OOXML_NAMESPACES = { - "http://schemas.openxmlformats.org/officeDocument/2006/math", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships", - "http://schemas.openxmlformats.org/schemaLibrary/2006/main", - "http://schemas.openxmlformats.org/drawingml/2006/main", - "http://schemas.openxmlformats.org/drawingml/2006/chart", - "http://schemas.openxmlformats.org/drawingml/2006/chartDrawing", - "http://schemas.openxmlformats.org/drawingml/2006/diagram", - "http://schemas.openxmlformats.org/drawingml/2006/picture", - "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", - "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", - "http://schemas.openxmlformats.org/wordprocessingml/2006/main", - "http://schemas.openxmlformats.org/presentationml/2006/main", - "http://schemas.openxmlformats.org/spreadsheetml/2006/main", - "http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes", - "http://www.w3.org/XML/1998/namespace", - } - - def __init__(self, unpacked_dir, original_file, verbose=False): - self.unpacked_dir = Path(unpacked_dir).resolve() - self.original_file = Path(original_file) - self.verbose = verbose - - # Set schemas directory - self.schemas_dir = Path(__file__).parent.parent.parent / "schemas" - - # Get all XML and .rels files - patterns = ["*.xml", "*.rels"] - self.xml_files = [ - f for pattern in patterns for f in self.unpacked_dir.rglob(pattern) - ] - - if not self.xml_files: - print(f"Warning: No XML files found in {self.unpacked_dir}") - - def validate(self): - """Run all validation checks and return True if all pass.""" - raise NotImplementedError("Subclasses must implement the validate method") - - def validate_xml(self): - """Validate that all XML files are well-formed.""" - errors = [] - - for xml_file in self.xml_files: - try: - # Try to parse the XML file - lxml.etree.parse(str(xml_file)) - except lxml.etree.XMLSyntaxError as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {e.lineno}: {e.msg}" - ) - except Exception as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Unexpected error: {str(e)}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} XML violations:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - All XML files are well-formed") - return True - - def validate_namespaces(self): - """Validate that namespace prefixes in Ignorable attributes are declared.""" - errors = [] - - for xml_file in self.xml_files: - try: - root = lxml.etree.parse(str(xml_file)).getroot() - declared = set(root.nsmap.keys()) - {None} # Exclude default namespace - - for attr_val in [ - v for k, v in root.attrib.items() if k.endswith("Ignorable") - ]: - undeclared = set(attr_val.split()) - declared - errors.extend( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Namespace '{ns}' in Ignorable but not declared" - for ns in undeclared - ) - except lxml.etree.XMLSyntaxError: - continue - - if errors: - print(f"FAILED - {len(errors)} namespace issues:") - for error in errors: - print(error) - return False - if self.verbose: - print("PASSED - All namespace prefixes properly declared") - return True - - def validate_unique_ids(self): - """Validate that specific IDs are unique according to OOXML requirements.""" - errors = [] - global_ids = {} # Track globally unique IDs across all files - - for xml_file in self.xml_files: - try: - root = lxml.etree.parse(str(xml_file)).getroot() - file_ids = {} # Track IDs that must be unique within this file - - # Remove all mc:AlternateContent elements from the tree - mc_elements = root.xpath( - ".//mc:AlternateContent", namespaces={"mc": self.MC_NAMESPACE} - ) - for elem in mc_elements: - elem.getparent().remove(elem) - - # Now check IDs in the cleaned tree - for elem in root.iter(): - # Get the element name without namespace - tag = ( - elem.tag.split("}")[-1].lower() - if "}" in elem.tag - else elem.tag.lower() - ) - - # Check if this element type has ID uniqueness requirements - if tag in self.UNIQUE_ID_REQUIREMENTS: - attr_name, scope = self.UNIQUE_ID_REQUIREMENTS[tag] - - # Look for the specified attribute - id_value = None - for attr, value in elem.attrib.items(): - attr_local = ( - attr.split("}")[-1].lower() - if "}" in attr - else attr.lower() - ) - if attr_local == attr_name: - id_value = value - break - - if id_value is not None: - if scope == "global": - # Check global uniqueness - if id_value in global_ids: - prev_file, prev_line, prev_tag = global_ids[ - id_value - ] - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {elem.sourceline}: Global ID '{id_value}' in <{tag}> " - f"already used in {prev_file} at line {prev_line} in <{prev_tag}>" - ) - else: - global_ids[id_value] = ( - xml_file.relative_to(self.unpacked_dir), - elem.sourceline, - tag, - ) - elif scope == "file": - # Check file-level uniqueness - key = (tag, attr_name) - if key not in file_ids: - file_ids[key] = {} - - if id_value in file_ids[key]: - prev_line = file_ids[key][id_value] - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {elem.sourceline}: Duplicate {attr_name}='{id_value}' in <{tag}> " - f"(first occurrence at line {prev_line})" - ) - else: - file_ids[key][id_value] = elem.sourceline - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} ID uniqueness violations:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - All required IDs are unique") - return True - - def validate_file_references(self): - """ - Validate that all .rels files properly reference files and that all files are referenced. - """ - errors = [] - - # Find all .rels files - rels_files = list(self.unpacked_dir.rglob("*.rels")) - - if not rels_files: - if self.verbose: - print("PASSED - No .rels files found") - return True - - # Get all files in the unpacked directory (excluding reference files) - all_files = [] - for file_path in self.unpacked_dir.rglob("*"): - if ( - file_path.is_file() - and file_path.name != "[Content_Types].xml" - and not file_path.name.endswith(".rels") - ): # This file is not referenced by .rels - all_files.append(file_path.resolve()) - - # Track all files that are referenced by any .rels file - all_referenced_files = set() - - if self.verbose: - print( - f"Found {len(rels_files)} .rels files and {len(all_files)} target files" - ) - - # Check each .rels file - for rels_file in rels_files: - try: - # Parse relationships file - rels_root = lxml.etree.parse(str(rels_file)).getroot() - - # Get the directory where this .rels file is located - rels_dir = rels_file.parent - - # Find all relationships and their targets - referenced_files = set() - broken_refs = [] - - for rel in rels_root.findall( - ".//ns:Relationship", - namespaces={"ns": self.PACKAGE_RELATIONSHIPS_NAMESPACE}, - ): - target = rel.get("Target") - if target and not target.startswith( - ("http", "mailto:") - ): # Skip external URLs - # Resolve the target path relative to the .rels file location - if rels_file.name == ".rels": - # Root .rels file - targets are relative to unpacked_dir - target_path = self.unpacked_dir / target - else: - # Other .rels files - targets are relative to their parent's parent - # e.g., word/_rels/document.xml.rels -> targets relative to word/ - base_dir = rels_dir.parent - target_path = base_dir / target - - # Normalize the path and check if it exists - try: - target_path = target_path.resolve() - if target_path.exists() and target_path.is_file(): - referenced_files.add(target_path) - all_referenced_files.add(target_path) - else: - broken_refs.append((target, rel.sourceline)) - except (OSError, ValueError): - broken_refs.append((target, rel.sourceline)) - - # Report broken references - if broken_refs: - rel_path = rels_file.relative_to(self.unpacked_dir) - for broken_ref, line_num in broken_refs: - errors.append( - f" {rel_path}: Line {line_num}: Broken reference to {broken_ref}" - ) - - except Exception as e: - rel_path = rels_file.relative_to(self.unpacked_dir) - errors.append(f" Error parsing {rel_path}: {e}") - - # Check for unreferenced files (files that exist but are not referenced anywhere) - unreferenced_files = set(all_files) - all_referenced_files - - if unreferenced_files: - for unref_file in sorted(unreferenced_files): - unref_rel_path = unref_file.relative_to(self.unpacked_dir) - errors.append(f" Unreferenced file: {unref_rel_path}") - - if errors: - print(f"FAILED - Found {len(errors)} relationship validation errors:") - for error in errors: - print(error) - print( - "CRITICAL: These errors will cause the document to appear corrupt. " - + "Broken references MUST be fixed, " - + "and unreferenced files MUST be referenced or removed." - ) - return False - else: - if self.verbose: - print( - "PASSED - All references are valid and all files are properly referenced" - ) - return True - - def validate_all_relationship_ids(self): - """ - Validate that all r:id attributes in XML files reference existing IDs - in their corresponding .rels files, and optionally validate relationship types. - """ - import lxml.etree - - errors = [] - - # Process each XML file that might contain r:id references - for xml_file in self.xml_files: - # Skip .rels files themselves - if xml_file.suffix == ".rels": - continue - - # Determine the corresponding .rels file - # For dir/file.xml, it's dir/_rels/file.xml.rels - rels_dir = xml_file.parent / "_rels" - rels_file = rels_dir / f"{xml_file.name}.rels" - - # Skip if there's no corresponding .rels file (that's okay) - if not rels_file.exists(): - continue - - try: - # Parse the .rels file to get valid relationship IDs and their types - rels_root = lxml.etree.parse(str(rels_file)).getroot() - rid_to_type = {} - - for rel in rels_root.findall( - f".//{{{self.PACKAGE_RELATIONSHIPS_NAMESPACE}}}Relationship" - ): - rid = rel.get("Id") - rel_type = rel.get("Type", "") - if rid: - # Check for duplicate rIds - if rid in rid_to_type: - rels_rel_path = rels_file.relative_to(self.unpacked_dir) - errors.append( - f" {rels_rel_path}: Line {rel.sourceline}: " - f"Duplicate relationship ID '{rid}' (IDs must be unique)" - ) - # Extract just the type name from the full URL - type_name = ( - rel_type.split("/")[-1] if "/" in rel_type else rel_type - ) - rid_to_type[rid] = type_name - - # Parse the XML file to find all r:id references - xml_root = lxml.etree.parse(str(xml_file)).getroot() - - # Find all elements with r:id attributes - for elem in xml_root.iter(): - # Check for r:id attribute (relationship ID) - rid_attr = elem.get(f"{{{self.OFFICE_RELATIONSHIPS_NAMESPACE}}}id") - if rid_attr: - xml_rel_path = xml_file.relative_to(self.unpacked_dir) - elem_name = ( - elem.tag.split("}")[-1] if "}" in elem.tag else elem.tag - ) - - # Check if the ID exists - if rid_attr not in rid_to_type: - errors.append( - f" {xml_rel_path}: Line {elem.sourceline}: " - f"<{elem_name}> references non-existent relationship '{rid_attr}' " - f"(valid IDs: {', '.join(sorted(rid_to_type.keys())[:5])}{'...' if len(rid_to_type) > 5 else ''})" - ) - # Check if we have type expectations for this element - elif self.ELEMENT_RELATIONSHIP_TYPES: - expected_type = self._get_expected_relationship_type( - elem_name - ) - if expected_type: - actual_type = rid_to_type[rid_attr] - # Check if the actual type matches or contains the expected type - if expected_type not in actual_type.lower(): - errors.append( - f" {xml_rel_path}: Line {elem.sourceline}: " - f"<{elem_name}> references '{rid_attr}' which points to '{actual_type}' " - f"but should point to a '{expected_type}' relationship" - ) - - except Exception as e: - xml_rel_path = xml_file.relative_to(self.unpacked_dir) - errors.append(f" Error processing {xml_rel_path}: {e}") - - if errors: - print(f"FAILED - Found {len(errors)} relationship ID reference errors:") - for error in errors: - print(error) - print("\nThese ID mismatches will cause the document to appear corrupt!") - return False - else: - if self.verbose: - print("PASSED - All relationship ID references are valid") - return True - - def _get_expected_relationship_type(self, element_name): - """ - Get the expected relationship type for an element. - First checks the explicit mapping, then tries pattern detection. - """ - # Normalize element name to lowercase - elem_lower = element_name.lower() - - # Check explicit mapping first - if elem_lower in self.ELEMENT_RELATIONSHIP_TYPES: - return self.ELEMENT_RELATIONSHIP_TYPES[elem_lower] - - # Try pattern detection for common patterns - # Pattern 1: Elements ending in "Id" often expect a relationship of the prefix type - if elem_lower.endswith("id") and len(elem_lower) > 2: - # e.g., "sldId" -> "sld", "sldMasterId" -> "sldMaster" - prefix = elem_lower[:-2] # Remove "id" - # Check if this might be a compound like "sldMasterId" - if prefix.endswith("master"): - return prefix.lower() - elif prefix.endswith("layout"): - return prefix.lower() - else: - # Simple case like "sldId" -> "slide" - # Common transformations - if prefix == "sld": - return "slide" - return prefix.lower() - - # Pattern 2: Elements ending in "Reference" expect a relationship of the prefix type - if elem_lower.endswith("reference") and len(elem_lower) > 9: - prefix = elem_lower[:-9] # Remove "reference" - return prefix.lower() - - return None - - def validate_content_types(self): - """Validate that all content files are properly declared in [Content_Types].xml.""" - errors = [] - - # Find [Content_Types].xml file - content_types_file = self.unpacked_dir / "[Content_Types].xml" - if not content_types_file.exists(): - print("FAILED - [Content_Types].xml file not found") - return False - - try: - # Parse and get all declared parts and extensions - root = lxml.etree.parse(str(content_types_file)).getroot() - declared_parts = set() - declared_extensions = set() - - # Get Override declarations (specific files) - for override in root.findall( - f".//{{{self.CONTENT_TYPES_NAMESPACE}}}Override" - ): - part_name = override.get("PartName") - if part_name is not None: - declared_parts.add(part_name.lstrip("/")) - - # Get Default declarations (by extension) - for default in root.findall( - f".//{{{self.CONTENT_TYPES_NAMESPACE}}}Default" - ): - extension = default.get("Extension") - if extension is not None: - declared_extensions.add(extension.lower()) - - # Root elements that require content type declaration - declarable_roots = { - "sld", - "sldLayout", - "sldMaster", - "presentation", # PowerPoint - "document", # Word - "workbook", - "worksheet", # Excel - "theme", # Common - } - - # Common media file extensions that should be declared - media_extensions = { - "png": "image/png", - "jpg": "image/jpeg", - "jpeg": "image/jpeg", - "gif": "image/gif", - "bmp": "image/bmp", - "tiff": "image/tiff", - "wmf": "image/x-wmf", - "emf": "image/x-emf", - } - - # Get all files in the unpacked directory - all_files = list(self.unpacked_dir.rglob("*")) - all_files = [f for f in all_files if f.is_file()] - - # Check all XML files for Override declarations - for xml_file in self.xml_files: - path_str = str(xml_file.relative_to(self.unpacked_dir)).replace( - "\\", "/" - ) - - # Skip non-content files - if any( - skip in path_str - for skip in [".rels", "[Content_Types]", "docProps/", "_rels/"] - ): - continue - - try: - root_tag = lxml.etree.parse(str(xml_file)).getroot().tag - root_name = root_tag.split("}")[-1] if "}" in root_tag else root_tag - - if root_name in declarable_roots and path_str not in declared_parts: - errors.append( - f" {path_str}: File with <{root_name}> root not declared in [Content_Types].xml" - ) - - except Exception: - continue # Skip unparseable files - - # Check all non-XML files for Default extension declarations - for file_path in all_files: - # Skip XML files and metadata files (already checked above) - if file_path.suffix.lower() in {".xml", ".rels"}: - continue - if file_path.name == "[Content_Types].xml": - continue - if "_rels" in file_path.parts or "docProps" in file_path.parts: - continue - - extension = file_path.suffix.lstrip(".").lower() - if extension and extension not in declared_extensions: - # Check if it's a known media extension that should be declared - if extension in media_extensions: - relative_path = file_path.relative_to(self.unpacked_dir) - errors.append( - f' {relative_path}: File with extension \'{extension}\' not declared in [Content_Types].xml - should add: ' - ) - - except Exception as e: - errors.append(f" Error parsing [Content_Types].xml: {e}") - - if errors: - print(f"FAILED - Found {len(errors)} content type declaration errors:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print( - "PASSED - All content files are properly declared in [Content_Types].xml" - ) - return True - - def validate_file_against_xsd(self, xml_file, verbose=False): - """Validate a single XML file against XSD schema, comparing with original. - - Args: - xml_file: Path to XML file to validate - verbose: Enable verbose output - - Returns: - tuple: (is_valid, new_errors_set) where is_valid is True/False/None (skipped) - """ - # Resolve both paths to handle symlinks - xml_file = Path(xml_file).resolve() - unpacked_dir = self.unpacked_dir.resolve() - - # Validate current file - is_valid, current_errors = self._validate_single_file_xsd( - xml_file, unpacked_dir - ) - - if is_valid is None: - return None, set() # Skipped - elif is_valid: - return True, set() # Valid, no errors - - # Get errors from original file for this specific file - original_errors = self._get_original_file_errors(xml_file) - - # Compare with original (both are guaranteed to be sets here) - assert current_errors is not None - new_errors = current_errors - original_errors - - if new_errors: - if verbose: - relative_path = xml_file.relative_to(unpacked_dir) - print(f"FAILED - {relative_path}: {len(new_errors)} new error(s)") - for error in list(new_errors)[:3]: - truncated = error[:250] + "..." if len(error) > 250 else error - print(f" - {truncated}") - return False, new_errors - else: - # All errors existed in original - if verbose: - print( - f"PASSED - No new errors (original had {len(current_errors)} errors)" - ) - return True, set() - - def validate_against_xsd(self): - """Validate XML files against XSD schemas, showing only new errors compared to original.""" - new_errors = [] - original_error_count = 0 - valid_count = 0 - skipped_count = 0 - - for xml_file in self.xml_files: - relative_path = str(xml_file.relative_to(self.unpacked_dir)) - is_valid, new_file_errors = self.validate_file_against_xsd( - xml_file, verbose=False - ) - - if is_valid is None: - skipped_count += 1 - continue - elif is_valid and not new_file_errors: - valid_count += 1 - continue - elif is_valid: - # Had errors but all existed in original - original_error_count += 1 - valid_count += 1 - continue - - # Has new errors - new_errors.append(f" {relative_path}: {len(new_file_errors)} new error(s)") - for error in list(new_file_errors)[:3]: # Show first 3 errors - new_errors.append( - f" - {error[:250]}..." if len(error) > 250 else f" - {error}" - ) - - # Print summary - if self.verbose: - print(f"Validated {len(self.xml_files)} files:") - print(f" - Valid: {valid_count}") - print(f" - Skipped (no schema): {skipped_count}") - if original_error_count: - print(f" - With original errors (ignored): {original_error_count}") - print( - f" - With NEW errors: {len(new_errors) > 0 and len([e for e in new_errors if not e.startswith(' ')]) or 0}" - ) - - if new_errors: - print("\nFAILED - Found NEW validation errors:") - for error in new_errors: - print(error) - return False - else: - if self.verbose: - print("\nPASSED - No new XSD validation errors introduced") - return True - - def _get_schema_path(self, xml_file): - """Determine the appropriate schema path for an XML file.""" - # Check exact filename match - if xml_file.name in self.SCHEMA_MAPPINGS: - return self.schemas_dir / self.SCHEMA_MAPPINGS[xml_file.name] - - # Check .rels files - if xml_file.suffix == ".rels": - return self.schemas_dir / self.SCHEMA_MAPPINGS[".rels"] - - # Check chart files - if "charts/" in str(xml_file) and xml_file.name.startswith("chart"): - return self.schemas_dir / self.SCHEMA_MAPPINGS["chart"] - - # Check theme files - if "theme/" in str(xml_file) and xml_file.name.startswith("theme"): - return self.schemas_dir / self.SCHEMA_MAPPINGS["theme"] - - # Check if file is in a main content folder and use appropriate schema - if xml_file.parent.name in self.MAIN_CONTENT_FOLDERS: - return self.schemas_dir / self.SCHEMA_MAPPINGS[xml_file.parent.name] - - return None - - def _clean_ignorable_namespaces(self, xml_doc): - """Remove attributes and elements not in allowed namespaces.""" - # Create a clean copy - xml_string = lxml.etree.tostring(xml_doc, encoding="unicode") - xml_copy = lxml.etree.fromstring(xml_string) - - # Remove attributes not in allowed namespaces - for elem in xml_copy.iter(): - attrs_to_remove = [] - - for attr in elem.attrib: - # Check if attribute is from a namespace other than allowed ones - if "{" in attr: - ns = attr.split("}")[0][1:] - if ns not in self.OOXML_NAMESPACES: - attrs_to_remove.append(attr) - - # Remove collected attributes - for attr in attrs_to_remove: - del elem.attrib[attr] - - # Remove elements not in allowed namespaces - self._remove_ignorable_elements(xml_copy) - - return lxml.etree.ElementTree(xml_copy) - - def _remove_ignorable_elements(self, root): - """Recursively remove all elements not in allowed namespaces.""" - elements_to_remove = [] - - # Find elements to remove - for elem in list(root): - # Skip non-element nodes (comments, processing instructions, etc.) - if not hasattr(elem, "tag") or callable(elem.tag): - continue - - tag_str = str(elem.tag) - if tag_str.startswith("{"): - ns = tag_str.split("}")[0][1:] - if ns not in self.OOXML_NAMESPACES: - elements_to_remove.append(elem) - continue - - # Recursively clean child elements - self._remove_ignorable_elements(elem) - - # Remove collected elements - for elem in elements_to_remove: - root.remove(elem) - - def _preprocess_for_mc_ignorable(self, xml_doc): - """Preprocess XML to handle mc:Ignorable attribute properly.""" - # Remove mc:Ignorable attributes before validation - root = xml_doc.getroot() - - # Remove mc:Ignorable attribute from root - if f"{{{self.MC_NAMESPACE}}}Ignorable" in root.attrib: - del root.attrib[f"{{{self.MC_NAMESPACE}}}Ignorable"] - - return xml_doc - - def _validate_single_file_xsd(self, xml_file, base_path): - """Validate a single XML file against XSD schema. Returns (is_valid, errors_set).""" - schema_path = self._get_schema_path(xml_file) - if not schema_path: - return None, None # Skip file - - try: - # Load schema - with open(schema_path, "rb") as xsd_file: - parser = lxml.etree.XMLParser() - xsd_doc = lxml.etree.parse( - xsd_file, parser=parser, base_url=str(schema_path) - ) - schema = lxml.etree.XMLSchema(xsd_doc) - - # Load and preprocess XML - with open(xml_file, "r") as f: - xml_doc = lxml.etree.parse(f) - - xml_doc, _ = self._remove_template_tags_from_text_nodes(xml_doc) - xml_doc = self._preprocess_for_mc_ignorable(xml_doc) - - # Clean ignorable namespaces if needed - relative_path = xml_file.relative_to(base_path) - if ( - relative_path.parts - and relative_path.parts[0] in self.MAIN_CONTENT_FOLDERS - ): - xml_doc = self._clean_ignorable_namespaces(xml_doc) - - # Validate - if schema.validate(xml_doc): - return True, set() - else: - errors = set() - for error in schema.error_log: - # Store normalized error message (without line numbers for comparison) - errors.add(error.message) - return False, errors - - except Exception as e: - return False, {str(e)} - - def _get_original_file_errors(self, xml_file): - """Get XSD validation errors from a single file in the original document. - - Args: - xml_file: Path to the XML file in unpacked_dir to check - - Returns: - set: Set of error messages from the original file - """ - import tempfile - import zipfile - - # Resolve both paths to handle symlinks (e.g., /var vs /private/var on macOS) - xml_file = Path(xml_file).resolve() - unpacked_dir = self.unpacked_dir.resolve() - relative_path = xml_file.relative_to(unpacked_dir) - - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) - - # Extract original file - with zipfile.ZipFile(self.original_file, "r") as zip_ref: - zip_ref.extractall(temp_path) - - # Find corresponding file in original - original_xml_file = temp_path / relative_path - - if not original_xml_file.exists(): - # File didn't exist in original, so no original errors - return set() - - # Validate the specific file in original - is_valid, errors = self._validate_single_file_xsd( - original_xml_file, temp_path - ) - return errors if errors else set() - - def _remove_template_tags_from_text_nodes(self, xml_doc): - """Remove template tags from XML text nodes and collect warnings. - - Template tags follow the pattern {{ ... }} and are used as placeholders - for content replacement. They should be removed from text content before - XSD validation while preserving XML structure. - - Returns: - tuple: (cleaned_xml_doc, warnings_list) - """ - warnings = [] - template_pattern = re.compile(r"\{\{[^}]*\}\}") - - # Create a copy of the document to avoid modifying the original - xml_string = lxml.etree.tostring(xml_doc, encoding="unicode") - xml_copy = lxml.etree.fromstring(xml_string) - - def process_text_content(text, content_type): - if not text: - return text - matches = list(template_pattern.finditer(text)) - if matches: - for match in matches: - warnings.append( - f"Found template tag in {content_type}: {match.group()}" - ) - return template_pattern.sub("", text) - return text - - # Process all text nodes in the document - for elem in xml_copy.iter(): - # Skip processing if this is a w:t element - if not hasattr(elem, "tag") or callable(elem.tag): - continue - tag_str = str(elem.tag) - if tag_str.endswith("}t") or tag_str == "t": - continue - - elem.text = process_text_content(elem.text, "text content") - elem.tail = process_text_content(elem.tail, "tail content") - - return lxml.etree.ElementTree(xml_copy), warnings - - -if __name__ == "__main__": - raise RuntimeError("This module should not be run directly.") diff --git a/document-skills/docx/ooxml/scripts/validation/docx.py b/document-skills/docx/ooxml/scripts/validation/docx.py deleted file mode 100644 index 602c47087..000000000 --- a/document-skills/docx/ooxml/scripts/validation/docx.py +++ /dev/null @@ -1,274 +0,0 @@ -""" -Validator for Word document XML files against XSD schemas. -""" - -import re -import tempfile -import zipfile - -import lxml.etree - -from .base import BaseSchemaValidator - - -class DOCXSchemaValidator(BaseSchemaValidator): - """Validator for Word document XML files against XSD schemas.""" - - # Word-specific namespace - WORD_2006_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" - - # Word-specific element to relationship type mappings - # Start with empty mapping - add specific cases as we discover them - ELEMENT_RELATIONSHIP_TYPES = {} - - def validate(self): - """Run all validation checks and return True if all pass.""" - # Test 0: XML well-formedness - if not self.validate_xml(): - return False - - # Test 1: Namespace declarations - all_valid = True - if not self.validate_namespaces(): - all_valid = False - - # Test 2: Unique IDs - if not self.validate_unique_ids(): - all_valid = False - - # Test 3: Relationship and file reference validation - if not self.validate_file_references(): - all_valid = False - - # Test 4: Content type declarations - if not self.validate_content_types(): - all_valid = False - - # Test 5: XSD schema validation - if not self.validate_against_xsd(): - all_valid = False - - # Test 6: Whitespace preservation - if not self.validate_whitespace_preservation(): - all_valid = False - - # Test 7: Deletion validation - if not self.validate_deletions(): - all_valid = False - - # Test 8: Insertion validation - if not self.validate_insertions(): - all_valid = False - - # Test 9: Relationship ID reference validation - if not self.validate_all_relationship_ids(): - all_valid = False - - # Count and compare paragraphs - self.compare_paragraph_counts() - - return all_valid - - def validate_whitespace_preservation(self): - """ - Validate that w:t elements with whitespace have xml:space='preserve'. - """ - errors = [] - - for xml_file in self.xml_files: - # Only check document.xml files - if xml_file.name != "document.xml": - continue - - try: - root = lxml.etree.parse(str(xml_file)).getroot() - - # Find all w:t elements - for elem in root.iter(f"{{{self.WORD_2006_NAMESPACE}}}t"): - if elem.text: - text = elem.text - # Check if text starts or ends with whitespace - if re.match(r"^\s.*", text) or re.match(r".*\s$", text): - # Check if xml:space="preserve" attribute exists - xml_space_attr = f"{{{self.XML_NAMESPACE}}}space" - if ( - xml_space_attr not in elem.attrib - or elem.attrib[xml_space_attr] != "preserve" - ): - # Show a preview of the text - text_preview = ( - repr(text)[:50] + "..." - if len(repr(text)) > 50 - else repr(text) - ) - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {elem.sourceline}: w:t element with whitespace missing xml:space='preserve': {text_preview}" - ) - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} whitespace preservation violations:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - All whitespace is properly preserved") - return True - - def validate_deletions(self): - """ - Validate that w:t elements are not within w:del elements. - For some reason, XSD validation does not catch this, so we do it manually. - """ - errors = [] - - for xml_file in self.xml_files: - # Only check document.xml files - if xml_file.name != "document.xml": - continue - - try: - root = lxml.etree.parse(str(xml_file)).getroot() - - # Find all w:t elements that are descendants of w:del elements - namespaces = {"w": self.WORD_2006_NAMESPACE} - xpath_expression = ".//w:del//w:t" - problematic_t_elements = root.xpath( - xpath_expression, namespaces=namespaces - ) - for t_elem in problematic_t_elements: - if t_elem.text: - # Show a preview of the text - text_preview = ( - repr(t_elem.text)[:50] + "..." - if len(repr(t_elem.text)) > 50 - else repr(t_elem.text) - ) - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {t_elem.sourceline}: found within : {text_preview}" - ) - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} deletion validation violations:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - No w:t elements found within w:del elements") - return True - - def count_paragraphs_in_unpacked(self): - """Count the number of paragraphs in the unpacked document.""" - count = 0 - - for xml_file in self.xml_files: - # Only check document.xml files - if xml_file.name != "document.xml": - continue - - try: - root = lxml.etree.parse(str(xml_file)).getroot() - # Count all w:p elements - paragraphs = root.findall(f".//{{{self.WORD_2006_NAMESPACE}}}p") - count = len(paragraphs) - except Exception as e: - print(f"Error counting paragraphs in unpacked document: {e}") - - return count - - def count_paragraphs_in_original(self): - """Count the number of paragraphs in the original docx file.""" - count = 0 - - try: - # Create temporary directory to unpack original - with tempfile.TemporaryDirectory() as temp_dir: - # Unpack original docx - with zipfile.ZipFile(self.original_file, "r") as zip_ref: - zip_ref.extractall(temp_dir) - - # Parse document.xml - doc_xml_path = temp_dir + "/word/document.xml" - root = lxml.etree.parse(doc_xml_path).getroot() - - # Count all w:p elements - paragraphs = root.findall(f".//{{{self.WORD_2006_NAMESPACE}}}p") - count = len(paragraphs) - - except Exception as e: - print(f"Error counting paragraphs in original document: {e}") - - return count - - def validate_insertions(self): - """ - Validate that w:delText elements are not within w:ins elements. - w:delText is only allowed in w:ins if nested within a w:del. - """ - errors = [] - - for xml_file in self.xml_files: - if xml_file.name != "document.xml": - continue - - try: - root = lxml.etree.parse(str(xml_file)).getroot() - namespaces = {"w": self.WORD_2006_NAMESPACE} - - # Find w:delText in w:ins that are NOT within w:del - invalid_elements = root.xpath( - ".//w:ins//w:delText[not(ancestor::w:del)]", - namespaces=namespaces - ) - - for elem in invalid_elements: - text_preview = ( - repr(elem.text or "")[:50] + "..." - if len(repr(elem.text or "")) > 50 - else repr(elem.text or "") - ) - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {elem.sourceline}: within : {text_preview}" - ) - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} insertion validation violations:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - No w:delText elements within w:ins elements") - return True - - def compare_paragraph_counts(self): - """Compare paragraph counts between original and new document.""" - original_count = self.count_paragraphs_in_original() - new_count = self.count_paragraphs_in_unpacked() - - diff = new_count - original_count - diff_str = f"+{diff}" if diff > 0 else str(diff) - print(f"\nParagraphs: {original_count} → {new_count} ({diff_str})") - - -if __name__ == "__main__": - raise RuntimeError("This module should not be run directly.") diff --git a/document-skills/docx/ooxml/scripts/validation/pptx.py b/document-skills/docx/ooxml/scripts/validation/pptx.py deleted file mode 100644 index 66d5b1e2d..000000000 --- a/document-skills/docx/ooxml/scripts/validation/pptx.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Validator for PowerPoint presentation XML files against XSD schemas. -""" - -import re - -from .base import BaseSchemaValidator - - -class PPTXSchemaValidator(BaseSchemaValidator): - """Validator for PowerPoint presentation XML files against XSD schemas.""" - - # PowerPoint presentation namespace - PRESENTATIONML_NAMESPACE = ( - "http://schemas.openxmlformats.org/presentationml/2006/main" - ) - - # PowerPoint-specific element to relationship type mappings - ELEMENT_RELATIONSHIP_TYPES = { - "sldid": "slide", - "sldmasterid": "slidemaster", - "notesmasterid": "notesmaster", - "sldlayoutid": "slidelayout", - "themeid": "theme", - "tablestyleid": "tablestyles", - } - - def validate(self): - """Run all validation checks and return True if all pass.""" - # Test 0: XML well-formedness - if not self.validate_xml(): - return False - - # Test 1: Namespace declarations - all_valid = True - if not self.validate_namespaces(): - all_valid = False - - # Test 2: Unique IDs - if not self.validate_unique_ids(): - all_valid = False - - # Test 3: UUID ID validation - if not self.validate_uuid_ids(): - all_valid = False - - # Test 4: Relationship and file reference validation - if not self.validate_file_references(): - all_valid = False - - # Test 5: Slide layout ID validation - if not self.validate_slide_layout_ids(): - all_valid = False - - # Test 6: Content type declarations - if not self.validate_content_types(): - all_valid = False - - # Test 7: XSD schema validation - if not self.validate_against_xsd(): - all_valid = False - - # Test 8: Notes slide reference validation - if not self.validate_notes_slide_references(): - all_valid = False - - # Test 9: Relationship ID reference validation - if not self.validate_all_relationship_ids(): - all_valid = False - - # Test 10: Duplicate slide layout references validation - if not self.validate_no_duplicate_slide_layouts(): - all_valid = False - - return all_valid - - def validate_uuid_ids(self): - """Validate that ID attributes that look like UUIDs contain only hex values.""" - import lxml.etree - - errors = [] - # UUID pattern: 8-4-4-4-12 hex digits with optional braces/hyphens - uuid_pattern = re.compile( - r"^[\{\(]?[0-9A-Fa-f]{8}-?[0-9A-Fa-f]{4}-?[0-9A-Fa-f]{4}-?[0-9A-Fa-f]{4}-?[0-9A-Fa-f]{12}[\}\)]?$" - ) - - for xml_file in self.xml_files: - try: - root = lxml.etree.parse(str(xml_file)).getroot() - - # Check all elements for ID attributes - for elem in root.iter(): - for attr, value in elem.attrib.items(): - # Check if this is an ID attribute - attr_name = attr.split("}")[-1].lower() - if attr_name == "id" or attr_name.endswith("id"): - # Check if value looks like a UUID (has the right length and pattern structure) - if self._looks_like_uuid(value): - # Validate that it contains only hex characters in the right positions - if not uuid_pattern.match(value): - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: " - f"Line {elem.sourceline}: ID '{value}' appears to be a UUID but contains invalid hex characters" - ) - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {xml_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} UUID ID validation errors:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - All UUID-like IDs contain valid hex values") - return True - - def _looks_like_uuid(self, value): - """Check if a value has the general structure of a UUID.""" - # Remove common UUID delimiters - clean_value = value.strip("{}()").replace("-", "") - # Check if it's 32 hex-like characters (could include invalid hex chars) - return len(clean_value) == 32 and all(c.isalnum() for c in clean_value) - - def validate_slide_layout_ids(self): - """Validate that sldLayoutId elements in slide masters reference valid slide layouts.""" - import lxml.etree - - errors = [] - - # Find all slide master files - slide_masters = list(self.unpacked_dir.glob("ppt/slideMasters/*.xml")) - - if not slide_masters: - if self.verbose: - print("PASSED - No slide masters found") - return True - - for slide_master in slide_masters: - try: - # Parse the slide master file - root = lxml.etree.parse(str(slide_master)).getroot() - - # Find the corresponding _rels file for this slide master - rels_file = slide_master.parent / "_rels" / f"{slide_master.name}.rels" - - if not rels_file.exists(): - errors.append( - f" {slide_master.relative_to(self.unpacked_dir)}: " - f"Missing relationships file: {rels_file.relative_to(self.unpacked_dir)}" - ) - continue - - # Parse the relationships file - rels_root = lxml.etree.parse(str(rels_file)).getroot() - - # Build a set of valid relationship IDs that point to slide layouts - valid_layout_rids = set() - for rel in rels_root.findall( - f".//{{{self.PACKAGE_RELATIONSHIPS_NAMESPACE}}}Relationship" - ): - rel_type = rel.get("Type", "") - if "slideLayout" in rel_type: - valid_layout_rids.add(rel.get("Id")) - - # Find all sldLayoutId elements in the slide master - for sld_layout_id in root.findall( - f".//{{{self.PRESENTATIONML_NAMESPACE}}}sldLayoutId" - ): - r_id = sld_layout_id.get( - f"{{{self.OFFICE_RELATIONSHIPS_NAMESPACE}}}id" - ) - layout_id = sld_layout_id.get("id") - - if r_id and r_id not in valid_layout_rids: - errors.append( - f" {slide_master.relative_to(self.unpacked_dir)}: " - f"Line {sld_layout_id.sourceline}: sldLayoutId with id='{layout_id}' " - f"references r:id='{r_id}' which is not found in slide layout relationships" - ) - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {slide_master.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print(f"FAILED - Found {len(errors)} slide layout ID validation errors:") - for error in errors: - print(error) - print( - "Remove invalid references or add missing slide layouts to the relationships file." - ) - return False - else: - if self.verbose: - print("PASSED - All slide layout IDs reference valid slide layouts") - return True - - def validate_no_duplicate_slide_layouts(self): - """Validate that each slide has exactly one slideLayout reference.""" - import lxml.etree - - errors = [] - slide_rels_files = list(self.unpacked_dir.glob("ppt/slides/_rels/*.xml.rels")) - - for rels_file in slide_rels_files: - try: - root = lxml.etree.parse(str(rels_file)).getroot() - - # Find all slideLayout relationships - layout_rels = [ - rel - for rel in root.findall( - f".//{{{self.PACKAGE_RELATIONSHIPS_NAMESPACE}}}Relationship" - ) - if "slideLayout" in rel.get("Type", "") - ] - - if len(layout_rels) > 1: - errors.append( - f" {rels_file.relative_to(self.unpacked_dir)}: has {len(layout_rels)} slideLayout references" - ) - - except Exception as e: - errors.append( - f" {rels_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - if errors: - print("FAILED - Found slides with duplicate slideLayout references:") - for error in errors: - print(error) - return False - else: - if self.verbose: - print("PASSED - All slides have exactly one slideLayout reference") - return True - - def validate_notes_slide_references(self): - """Validate that each notesSlide file is referenced by only one slide.""" - import lxml.etree - - errors = [] - notes_slide_references = {} # Track which slides reference each notesSlide - - # Find all slide relationship files - slide_rels_files = list(self.unpacked_dir.glob("ppt/slides/_rels/*.xml.rels")) - - if not slide_rels_files: - if self.verbose: - print("PASSED - No slide relationship files found") - return True - - for rels_file in slide_rels_files: - try: - # Parse the relationships file - root = lxml.etree.parse(str(rels_file)).getroot() - - # Find all notesSlide relationships - for rel in root.findall( - f".//{{{self.PACKAGE_RELATIONSHIPS_NAMESPACE}}}Relationship" - ): - rel_type = rel.get("Type", "") - if "notesSlide" in rel_type: - target = rel.get("Target", "") - if target: - # Normalize the target path to handle relative paths - normalized_target = target.replace("../", "") - - # Track which slide references this notesSlide - slide_name = rels_file.stem.replace( - ".xml", "" - ) # e.g., "slide1" - - if normalized_target not in notes_slide_references: - notes_slide_references[normalized_target] = [] - notes_slide_references[normalized_target].append( - (slide_name, rels_file) - ) - - except (lxml.etree.XMLSyntaxError, Exception) as e: - errors.append( - f" {rels_file.relative_to(self.unpacked_dir)}: Error: {e}" - ) - - # Check for duplicate references - for target, references in notes_slide_references.items(): - if len(references) > 1: - slide_names = [ref[0] for ref in references] - errors.append( - f" Notes slide '{target}' is referenced by multiple slides: {', '.join(slide_names)}" - ) - for slide_name, rels_file in references: - errors.append(f" - {rels_file.relative_to(self.unpacked_dir)}") - - if errors: - print( - f"FAILED - Found {len([e for e in errors if not e.startswith(' ')])} notes slide reference validation errors:" - ) - for error in errors: - print(error) - print("Each slide may optionally have its own slide file.") - return False - else: - if self.verbose: - print("PASSED - All notes slide references are unique") - return True - - -if __name__ == "__main__": - raise RuntimeError("This module should not be run directly.") diff --git a/document-skills/docx/ooxml/scripts/validation/redlining.py b/document-skills/docx/ooxml/scripts/validation/redlining.py deleted file mode 100644 index 7ed425edf..000000000 --- a/document-skills/docx/ooxml/scripts/validation/redlining.py +++ /dev/null @@ -1,279 +0,0 @@ -""" -Validator for tracked changes in Word documents. -""" - -import subprocess -import tempfile -import zipfile -from pathlib import Path - - -class RedliningValidator: - """Validator for tracked changes in Word documents.""" - - def __init__(self, unpacked_dir, original_docx, verbose=False): - self.unpacked_dir = Path(unpacked_dir) - self.original_docx = Path(original_docx) - self.verbose = verbose - self.namespaces = { - "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main" - } - - def validate(self): - """Main validation method that returns True if valid, False otherwise.""" - # Verify unpacked directory exists and has correct structure - modified_file = self.unpacked_dir / "word" / "document.xml" - if not modified_file.exists(): - print(f"FAILED - Modified document.xml not found at {modified_file}") - return False - - # First, check if there are any tracked changes by Claude to validate - try: - import xml.etree.ElementTree as ET - - tree = ET.parse(modified_file) - root = tree.getroot() - - # Check for w:del or w:ins tags authored by Claude - del_elements = root.findall(".//w:del", self.namespaces) - ins_elements = root.findall(".//w:ins", self.namespaces) - - # Filter to only include changes by Claude - claude_del_elements = [ - elem - for elem in del_elements - if elem.get(f"{{{self.namespaces['w']}}}author") == "Claude" - ] - claude_ins_elements = [ - elem - for elem in ins_elements - if elem.get(f"{{{self.namespaces['w']}}}author") == "Claude" - ] - - # Redlining validation is only needed if tracked changes by Claude have been used. - if not claude_del_elements and not claude_ins_elements: - if self.verbose: - print("PASSED - No tracked changes by Claude found.") - return True - - except Exception: - # If we can't parse the XML, continue with full validation - pass - - # Create temporary directory for unpacking original docx - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) - - # Unpack original docx - try: - with zipfile.ZipFile(self.original_docx, "r") as zip_ref: - zip_ref.extractall(temp_path) - except Exception as e: - print(f"FAILED - Error unpacking original docx: {e}") - return False - - original_file = temp_path / "word" / "document.xml" - if not original_file.exists(): - print( - f"FAILED - Original document.xml not found in {self.original_docx}" - ) - return False - - # Parse both XML files using xml.etree.ElementTree for redlining validation - try: - import xml.etree.ElementTree as ET - - modified_tree = ET.parse(modified_file) - modified_root = modified_tree.getroot() - original_tree = ET.parse(original_file) - original_root = original_tree.getroot() - except ET.ParseError as e: - print(f"FAILED - Error parsing XML files: {e}") - return False - - # Remove Claude's tracked changes from both documents - self._remove_claude_tracked_changes(original_root) - self._remove_claude_tracked_changes(modified_root) - - # Extract and compare text content - modified_text = self._extract_text_content(modified_root) - original_text = self._extract_text_content(original_root) - - if modified_text != original_text: - # Show detailed character-level differences for each paragraph - error_message = self._generate_detailed_diff( - original_text, modified_text - ) - print(error_message) - return False - - if self.verbose: - print("PASSED - All changes by Claude are properly tracked") - return True - - def _generate_detailed_diff(self, original_text, modified_text): - """Generate detailed word-level differences using git word diff.""" - error_parts = [ - "FAILED - Document text doesn't match after removing Claude's tracked changes", - "", - "Likely causes:", - " 1. Modified text inside another author's or tags", - " 2. Made edits without proper tracked changes", - " 3. Didn't nest inside when deleting another's insertion", - "", - "For pre-redlined documents, use correct patterns:", - " - To reject another's INSERTION: Nest inside their ", - " - To restore another's DELETION: Add new AFTER their ", - "", - ] - - # Show git word diff - git_diff = self._get_git_word_diff(original_text, modified_text) - if git_diff: - error_parts.extend(["Differences:", "============", git_diff]) - else: - error_parts.append("Unable to generate word diff (git not available)") - - return "\n".join(error_parts) - - def _get_git_word_diff(self, original_text, modified_text): - """Generate word diff using git with character-level precision.""" - try: - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) - - # Create two files - original_file = temp_path / "original.txt" - modified_file = temp_path / "modified.txt" - - original_file.write_text(original_text, encoding="utf-8") - modified_file.write_text(modified_text, encoding="utf-8") - - # Try character-level diff first for precise differences - result = subprocess.run( - [ - "git", - "diff", - "--word-diff=plain", - "--word-diff-regex=.", # Character-by-character diff - "-U0", # Zero lines of context - show only changed lines - "--no-index", - str(original_file), - str(modified_file), - ], - capture_output=True, - text=True, - ) - - if result.stdout.strip(): - # Clean up the output - remove git diff header lines - lines = result.stdout.split("\n") - # Skip the header lines (diff --git, index, +++, ---, @@) - content_lines = [] - in_content = False - for line in lines: - if line.startswith("@@"): - in_content = True - continue - if in_content and line.strip(): - content_lines.append(line) - - if content_lines: - return "\n".join(content_lines) - - # Fallback to word-level diff if character-level is too verbose - result = subprocess.run( - [ - "git", - "diff", - "--word-diff=plain", - "-U0", # Zero lines of context - "--no-index", - str(original_file), - str(modified_file), - ], - capture_output=True, - text=True, - ) - - if result.stdout.strip(): - lines = result.stdout.split("\n") - content_lines = [] - in_content = False - for line in lines: - if line.startswith("@@"): - in_content = True - continue - if in_content and line.strip(): - content_lines.append(line) - return "\n".join(content_lines) - - except (subprocess.CalledProcessError, FileNotFoundError, Exception): - # Git not available or other error, return None to use fallback - pass - - return None - - def _remove_claude_tracked_changes(self, root): - """Remove tracked changes authored by Claude from the XML root.""" - ins_tag = f"{{{self.namespaces['w']}}}ins" - del_tag = f"{{{self.namespaces['w']}}}del" - author_attr = f"{{{self.namespaces['w']}}}author" - - # Remove w:ins elements - for parent in root.iter(): - to_remove = [] - for child in parent: - if child.tag == ins_tag and child.get(author_attr) == "Claude": - to_remove.append(child) - for elem in to_remove: - parent.remove(elem) - - # Unwrap content in w:del elements where author is "Claude" - deltext_tag = f"{{{self.namespaces['w']}}}delText" - t_tag = f"{{{self.namespaces['w']}}}t" - - for parent in root.iter(): - to_process = [] - for child in parent: - if child.tag == del_tag and child.get(author_attr) == "Claude": - to_process.append((child, list(parent).index(child))) - - # Process in reverse order to maintain indices - for del_elem, del_index in reversed(to_process): - # Convert w:delText to w:t before moving - for elem in del_elem.iter(): - if elem.tag == deltext_tag: - elem.tag = t_tag - - # Move all children of w:del to its parent before removing w:del - for child in reversed(list(del_elem)): - parent.insert(del_index, child) - parent.remove(del_elem) - - def _extract_text_content(self, root): - """Extract text content from Word XML, preserving paragraph structure. - - Empty paragraphs are skipped to avoid false positives when tracked - insertions add only structural elements without text content. - """ - p_tag = f"{{{self.namespaces['w']}}}p" - t_tag = f"{{{self.namespaces['w']}}}t" - - paragraphs = [] - for p_elem in root.findall(f".//{p_tag}"): - # Get all text elements within this paragraph - text_parts = [] - for t_elem in p_elem.findall(f".//{t_tag}"): - if t_elem.text: - text_parts.append(t_elem.text) - paragraph_text = "".join(text_parts) - # Skip empty paragraphs - they don't affect content validation - if paragraph_text: - paragraphs.append(paragraph_text) - - return "\n".join(paragraphs) - - -if __name__ == "__main__": - raise RuntimeError("This module should not be run directly.") diff --git a/document-skills/docx/scripts/__init__.py b/document-skills/docx/scripts/__init__.py deleted file mode 100755 index bf9c56272..000000000 --- a/document-skills/docx/scripts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Make scripts directory a package for relative imports in tests diff --git a/document-skills/docx/scripts/document.py b/document-skills/docx/scripts/document.py deleted file mode 100755 index ae9328ddf..000000000 --- a/document-skills/docx/scripts/document.py +++ /dev/null @@ -1,1276 +0,0 @@ -#!/usr/bin/env python3 -""" -Library for working with Word documents: comments, tracked changes, and editing. - -Usage: - from skills.docx.scripts.document import Document - - # Initialize - doc = Document('workspace/unpacked') - doc = Document('workspace/unpacked', author="John Doe", initials="JD") - - # Find nodes - node = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "1"}) - node = doc["word/document.xml"].get_node(tag="w:p", line_number=10) - - # Add comments - doc.add_comment(start=node, end=node, text="Comment text") - doc.reply_to_comment(parent_comment_id=0, text="Reply text") - - # Suggest tracked changes - doc["word/document.xml"].suggest_deletion(node) # Delete content - doc["word/document.xml"].revert_insertion(ins_node) # Reject insertion - doc["word/document.xml"].revert_deletion(del_node) # Reject deletion - - # Save - doc.save() -""" - -import html -import random -import shutil -import tempfile -from datetime import datetime, timezone -from pathlib import Path - -from defusedxml import minidom -from ooxml.scripts.pack import pack_document -from ooxml.scripts.validation.docx import DOCXSchemaValidator -from ooxml.scripts.validation.redlining import RedliningValidator - -from .utilities import XMLEditor - -# Path to template files -TEMPLATE_DIR = Path(__file__).parent / "templates" - - -class DocxXMLEditor(XMLEditor): - """XMLEditor that automatically applies RSID, author, and date to new elements. - - Automatically adds attributes to elements that support them when inserting new content: - - w:rsidR, w:rsidRDefault, w:rsidP (for w:p and w:r elements) - - w:author and w:date (for w:ins, w:del, w:comment elements) - - w:id (for w:ins and w:del elements) - - Attributes: - dom (defusedxml.minidom.Document): The DOM document for direct manipulation - """ - - def __init__( - self, xml_path, rsid: str, author: str = "Claude", initials: str = "C" - ): - """Initialize with required RSID and optional author. - - Args: - xml_path: Path to XML file to edit - rsid: RSID to automatically apply to new elements - author: Author name for tracked changes and comments (default: "Claude") - initials: Author initials (default: "C") - """ - super().__init__(xml_path) - self.rsid = rsid - self.author = author - self.initials = initials - - def _get_next_change_id(self): - """Get the next available change ID by checking all tracked change elements.""" - max_id = -1 - for tag in ("w:ins", "w:del"): - elements = self.dom.getElementsByTagName(tag) - for elem in elements: - change_id = elem.getAttribute("w:id") - if change_id: - try: - max_id = max(max_id, int(change_id)) - except ValueError: - pass - return max_id + 1 - - def _ensure_w16du_namespace(self): - """Ensure w16du namespace is declared on the root element.""" - root = self.dom.documentElement - if not root.hasAttribute("xmlns:w16du"): # type: ignore - root.setAttribute( # type: ignore - "xmlns:w16du", - "http://schemas.microsoft.com/office/word/2023/wordml/word16du", - ) - - def _ensure_w16cex_namespace(self): - """Ensure w16cex namespace is declared on the root element.""" - root = self.dom.documentElement - if not root.hasAttribute("xmlns:w16cex"): # type: ignore - root.setAttribute( # type: ignore - "xmlns:w16cex", - "http://schemas.microsoft.com/office/word/2018/wordml/cex", - ) - - def _ensure_w14_namespace(self): - """Ensure w14 namespace is declared on the root element.""" - root = self.dom.documentElement - if not root.hasAttribute("xmlns:w14"): # type: ignore - root.setAttribute( # type: ignore - "xmlns:w14", - "http://schemas.microsoft.com/office/word/2010/wordml", - ) - - def _inject_attributes_to_nodes(self, nodes): - """Inject RSID, author, and date attributes into DOM nodes where applicable. - - Adds attributes to elements that support them: - - w:r: gets w:rsidR (or w:rsidDel if inside w:del) - - w:p: gets w:rsidR, w:rsidRDefault, w:rsidP, w14:paraId, w14:textId - - w:t: gets xml:space="preserve" if text has leading/trailing whitespace - - w:ins, w:del: get w:id, w:author, w:date, w16du:dateUtc - - w:comment: gets w:author, w:date, w:initials - - w16cex:commentExtensible: gets w16cex:dateUtc - - Args: - nodes: List of DOM nodes to process - """ - from datetime import datetime, timezone - - timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - def is_inside_deletion(elem): - """Check if element is inside a w:del element.""" - parent = elem.parentNode - while parent: - if parent.nodeType == parent.ELEMENT_NODE and parent.tagName == "w:del": - return True - parent = parent.parentNode - return False - - def add_rsid_to_p(elem): - if not elem.hasAttribute("w:rsidR"): - elem.setAttribute("w:rsidR", self.rsid) - if not elem.hasAttribute("w:rsidRDefault"): - elem.setAttribute("w:rsidRDefault", self.rsid) - if not elem.hasAttribute("w:rsidP"): - elem.setAttribute("w:rsidP", self.rsid) - # Add w14:paraId and w14:textId if not present - if not elem.hasAttribute("w14:paraId"): - self._ensure_w14_namespace() - elem.setAttribute("w14:paraId", _generate_hex_id()) - if not elem.hasAttribute("w14:textId"): - self._ensure_w14_namespace() - elem.setAttribute("w14:textId", _generate_hex_id()) - - def add_rsid_to_r(elem): - # Use w:rsidDel for inside , otherwise w:rsidR - if is_inside_deletion(elem): - if not elem.hasAttribute("w:rsidDel"): - elem.setAttribute("w:rsidDel", self.rsid) - else: - if not elem.hasAttribute("w:rsidR"): - elem.setAttribute("w:rsidR", self.rsid) - - def add_tracked_change_attrs(elem): - # Auto-assign w:id if not present - if not elem.hasAttribute("w:id"): - elem.setAttribute("w:id", str(self._get_next_change_id())) - if not elem.hasAttribute("w:author"): - elem.setAttribute("w:author", self.author) - if not elem.hasAttribute("w:date"): - elem.setAttribute("w:date", timestamp) - # Add w16du:dateUtc for tracked changes (same as w:date since we generate UTC timestamps) - if elem.tagName in ("w:ins", "w:del") and not elem.hasAttribute( - "w16du:dateUtc" - ): - self._ensure_w16du_namespace() - elem.setAttribute("w16du:dateUtc", timestamp) - - def add_comment_attrs(elem): - if not elem.hasAttribute("w:author"): - elem.setAttribute("w:author", self.author) - if not elem.hasAttribute("w:date"): - elem.setAttribute("w:date", timestamp) - if not elem.hasAttribute("w:initials"): - elem.setAttribute("w:initials", self.initials) - - def add_comment_extensible_date(elem): - # Add w16cex:dateUtc for comment extensible elements - if not elem.hasAttribute("w16cex:dateUtc"): - self._ensure_w16cex_namespace() - elem.setAttribute("w16cex:dateUtc", timestamp) - - def add_xml_space_to_t(elem): - # Add xml:space="preserve" to w:t if text has leading/trailing whitespace - if ( - elem.firstChild - and elem.firstChild.nodeType == elem.firstChild.TEXT_NODE - ): - text = elem.firstChild.data - if text and (text[0].isspace() or text[-1].isspace()): - if not elem.hasAttribute("xml:space"): - elem.setAttribute("xml:space", "preserve") - - for node in nodes: - if node.nodeType != node.ELEMENT_NODE: - continue - - # Handle the node itself - if node.tagName == "w:p": - add_rsid_to_p(node) - elif node.tagName == "w:r": - add_rsid_to_r(node) - elif node.tagName == "w:t": - add_xml_space_to_t(node) - elif node.tagName in ("w:ins", "w:del"): - add_tracked_change_attrs(node) - elif node.tagName == "w:comment": - add_comment_attrs(node) - elif node.tagName == "w16cex:commentExtensible": - add_comment_extensible_date(node) - - # Process descendants (getElementsByTagName doesn't return the element itself) - for elem in node.getElementsByTagName("w:p"): - add_rsid_to_p(elem) - for elem in node.getElementsByTagName("w:r"): - add_rsid_to_r(elem) - for elem in node.getElementsByTagName("w:t"): - add_xml_space_to_t(elem) - for tag in ("w:ins", "w:del"): - for elem in node.getElementsByTagName(tag): - add_tracked_change_attrs(elem) - for elem in node.getElementsByTagName("w:comment"): - add_comment_attrs(elem) - for elem in node.getElementsByTagName("w16cex:commentExtensible"): - add_comment_extensible_date(elem) - - def replace_node(self, elem, new_content): - """Replace node with automatic attribute injection.""" - nodes = super().replace_node(elem, new_content) - self._inject_attributes_to_nodes(nodes) - return nodes - - def insert_after(self, elem, xml_content): - """Insert after with automatic attribute injection.""" - nodes = super().insert_after(elem, xml_content) - self._inject_attributes_to_nodes(nodes) - return nodes - - def insert_before(self, elem, xml_content): - """Insert before with automatic attribute injection.""" - nodes = super().insert_before(elem, xml_content) - self._inject_attributes_to_nodes(nodes) - return nodes - - def append_to(self, elem, xml_content): - """Append to with automatic attribute injection.""" - nodes = super().append_to(elem, xml_content) - self._inject_attributes_to_nodes(nodes) - return nodes - - def revert_insertion(self, elem): - """Reject an insertion by wrapping its content in a deletion. - - Wraps all runs inside w:ins in w:del, converting w:t to w:delText. - Can process a single w:ins element or a container element with multiple w:ins. - - Args: - elem: Element to process (w:ins, w:p, w:body, etc.) - - Returns: - list: List containing the processed element(s) - - Raises: - ValueError: If the element contains no w:ins elements - - Example: - # Reject a single insertion - ins = doc["word/document.xml"].get_node(tag="w:ins", attrs={"w:id": "5"}) - doc["word/document.xml"].revert_insertion(ins) - - # Reject all insertions in a paragraph - para = doc["word/document.xml"].get_node(tag="w:p", line_number=42) - doc["word/document.xml"].revert_insertion(para) - """ - # Collect insertions - ins_elements = [] - if elem.tagName == "w:ins": - ins_elements.append(elem) - else: - ins_elements.extend(elem.getElementsByTagName("w:ins")) - - # Validate that there are insertions to reject - if not ins_elements: - raise ValueError( - f"revert_insertion requires w:ins elements. " - f"The provided element <{elem.tagName}> contains no insertions. " - ) - - # Process all insertions - wrap all children in w:del - for ins_elem in ins_elements: - runs = list(ins_elem.getElementsByTagName("w:r")) - if not runs: - continue - - # Create deletion wrapper - del_wrapper = self.dom.createElement("w:del") - - # Process each run - for run in runs: - # Convert w:t → w:delText and w:rsidR → w:rsidDel - if run.hasAttribute("w:rsidR"): - run.setAttribute("w:rsidDel", run.getAttribute("w:rsidR")) - run.removeAttribute("w:rsidR") - elif not run.hasAttribute("w:rsidDel"): - run.setAttribute("w:rsidDel", self.rsid) - - for t_elem in list(run.getElementsByTagName("w:t")): - del_text = self.dom.createElement("w:delText") - # Copy ALL child nodes (not just firstChild) to handle entities - while t_elem.firstChild: - del_text.appendChild(t_elem.firstChild) - for i in range(t_elem.attributes.length): - attr = t_elem.attributes.item(i) - del_text.setAttribute(attr.name, attr.value) - t_elem.parentNode.replaceChild(del_text, t_elem) - - # Move all children from ins to del wrapper - while ins_elem.firstChild: - del_wrapper.appendChild(ins_elem.firstChild) - - # Add del wrapper back to ins - ins_elem.appendChild(del_wrapper) - - # Inject attributes to the deletion wrapper - self._inject_attributes_to_nodes([del_wrapper]) - - return [elem] - - def revert_deletion(self, elem): - """Reject a deletion by re-inserting the deleted content. - - Creates w:ins elements after each w:del, copying deleted content and - converting w:delText back to w:t. - Can process a single w:del element or a container element with multiple w:del. - - Args: - elem: Element to process (w:del, w:p, w:body, etc.) - - Returns: - list: If elem is w:del, returns [elem, new_ins]. Otherwise returns [elem]. - - Raises: - ValueError: If the element contains no w:del elements - - Example: - # Reject a single deletion - returns [w:del, w:ins] - del_elem = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "3"}) - nodes = doc["word/document.xml"].revert_deletion(del_elem) - - # Reject all deletions in a paragraph - returns [para] - para = doc["word/document.xml"].get_node(tag="w:p", line_number=42) - nodes = doc["word/document.xml"].revert_deletion(para) - """ - # Collect deletions FIRST - before we modify the DOM - del_elements = [] - is_single_del = elem.tagName == "w:del" - - if is_single_del: - del_elements.append(elem) - else: - del_elements.extend(elem.getElementsByTagName("w:del")) - - # Validate that there are deletions to reject - if not del_elements: - raise ValueError( - f"revert_deletion requires w:del elements. " - f"The provided element <{elem.tagName}> contains no deletions. " - ) - - # Track created insertion (only relevant if elem is a single w:del) - created_insertion = None - - # Process all deletions - create insertions that copy the deleted content - for del_elem in del_elements: - # Clone the deleted runs and convert them to insertions - runs = list(del_elem.getElementsByTagName("w:r")) - if not runs: - continue - - # Create insertion wrapper - ins_elem = self.dom.createElement("w:ins") - - for run in runs: - # Clone the run - new_run = run.cloneNode(True) - - # Convert w:delText → w:t - for del_text in list(new_run.getElementsByTagName("w:delText")): - t_elem = self.dom.createElement("w:t") - # Copy ALL child nodes (not just firstChild) to handle entities - while del_text.firstChild: - t_elem.appendChild(del_text.firstChild) - for i in range(del_text.attributes.length): - attr = del_text.attributes.item(i) - t_elem.setAttribute(attr.name, attr.value) - del_text.parentNode.replaceChild(t_elem, del_text) - - # Update run attributes: w:rsidDel → w:rsidR - if new_run.hasAttribute("w:rsidDel"): - new_run.setAttribute("w:rsidR", new_run.getAttribute("w:rsidDel")) - new_run.removeAttribute("w:rsidDel") - elif not new_run.hasAttribute("w:rsidR"): - new_run.setAttribute("w:rsidR", self.rsid) - - ins_elem.appendChild(new_run) - - # Insert the new insertion after the deletion - nodes = self.insert_after(del_elem, ins_elem.toxml()) - - # If processing a single w:del, track the created insertion - if is_single_del and nodes: - created_insertion = nodes[0] - - # Return based on input type - if is_single_del and created_insertion: - return [elem, created_insertion] - else: - return [elem] - - @staticmethod - def suggest_paragraph(xml_content: str) -> str: - """Transform paragraph XML to add tracked change wrapping for insertion. - - Wraps runs in and adds to w:rPr in w:pPr for numbered lists. - - Args: - xml_content: XML string containing a element - - Returns: - str: Transformed XML with tracked change wrapping - """ - wrapper = f'{xml_content}' - doc = minidom.parseString(wrapper) - para = doc.getElementsByTagName("w:p")[0] - - # Ensure w:pPr exists - pPr_list = para.getElementsByTagName("w:pPr") - if not pPr_list: - pPr = doc.createElement("w:pPr") - para.insertBefore( - pPr, para.firstChild - ) if para.firstChild else para.appendChild(pPr) - else: - pPr = pPr_list[0] - - # Ensure w:rPr exists in w:pPr - rPr_list = pPr.getElementsByTagName("w:rPr") - if not rPr_list: - rPr = doc.createElement("w:rPr") - pPr.appendChild(rPr) - else: - rPr = rPr_list[0] - - # Add to w:rPr - ins_marker = doc.createElement("w:ins") - rPr.insertBefore( - ins_marker, rPr.firstChild - ) if rPr.firstChild else rPr.appendChild(ins_marker) - - # Wrap all non-pPr children in - ins_wrapper = doc.createElement("w:ins") - for child in [c for c in para.childNodes if c.nodeName != "w:pPr"]: - para.removeChild(child) - ins_wrapper.appendChild(child) - para.appendChild(ins_wrapper) - - return para.toxml() - - def suggest_deletion(self, elem): - """Mark a w:r or w:p element as deleted with tracked changes (in-place DOM manipulation). - - For w:r: wraps in , converts to , preserves w:rPr - For w:p (regular): wraps content in , converts to - For w:p (numbered list): adds to w:rPr in w:pPr, wraps content in - - Args: - elem: A w:r or w:p DOM element without existing tracked changes - - Returns: - Element: The modified element - - Raises: - ValueError: If element has existing tracked changes or invalid structure - """ - if elem.nodeName == "w:r": - # Check for existing w:delText - if elem.getElementsByTagName("w:delText"): - raise ValueError("w:r element already contains w:delText") - - # Convert w:t → w:delText - for t_elem in list(elem.getElementsByTagName("w:t")): - del_text = self.dom.createElement("w:delText") - # Copy ALL child nodes (not just firstChild) to handle entities - while t_elem.firstChild: - del_text.appendChild(t_elem.firstChild) - # Preserve attributes like xml:space - for i in range(t_elem.attributes.length): - attr = t_elem.attributes.item(i) - del_text.setAttribute(attr.name, attr.value) - t_elem.parentNode.replaceChild(del_text, t_elem) - - # Update run attributes: w:rsidR → w:rsidDel - if elem.hasAttribute("w:rsidR"): - elem.setAttribute("w:rsidDel", elem.getAttribute("w:rsidR")) - elem.removeAttribute("w:rsidR") - elif not elem.hasAttribute("w:rsidDel"): - elem.setAttribute("w:rsidDel", self.rsid) - - # Wrap in w:del - del_wrapper = self.dom.createElement("w:del") - parent = elem.parentNode - parent.insertBefore(del_wrapper, elem) - parent.removeChild(elem) - del_wrapper.appendChild(elem) - - # Inject attributes to the deletion wrapper - self._inject_attributes_to_nodes([del_wrapper]) - - return del_wrapper - - elif elem.nodeName == "w:p": - # Check for existing tracked changes - if elem.getElementsByTagName("w:ins") or elem.getElementsByTagName("w:del"): - raise ValueError("w:p element already contains tracked changes") - - # Check if it's a numbered list item - pPr_list = elem.getElementsByTagName("w:pPr") - is_numbered = pPr_list and pPr_list[0].getElementsByTagName("w:numPr") - - if is_numbered: - # Add to w:rPr in w:pPr - pPr = pPr_list[0] - rPr_list = pPr.getElementsByTagName("w:rPr") - - if not rPr_list: - rPr = self.dom.createElement("w:rPr") - pPr.appendChild(rPr) - else: - rPr = rPr_list[0] - - # Add marker - del_marker = self.dom.createElement("w:del") - rPr.insertBefore( - del_marker, rPr.firstChild - ) if rPr.firstChild else rPr.appendChild(del_marker) - - # Convert w:t → w:delText in all runs - for t_elem in list(elem.getElementsByTagName("w:t")): - del_text = self.dom.createElement("w:delText") - # Copy ALL child nodes (not just firstChild) to handle entities - while t_elem.firstChild: - del_text.appendChild(t_elem.firstChild) - # Preserve attributes like xml:space - for i in range(t_elem.attributes.length): - attr = t_elem.attributes.item(i) - del_text.setAttribute(attr.name, attr.value) - t_elem.parentNode.replaceChild(del_text, t_elem) - - # Update run attributes: w:rsidR → w:rsidDel - for run in elem.getElementsByTagName("w:r"): - if run.hasAttribute("w:rsidR"): - run.setAttribute("w:rsidDel", run.getAttribute("w:rsidR")) - run.removeAttribute("w:rsidR") - elif not run.hasAttribute("w:rsidDel"): - run.setAttribute("w:rsidDel", self.rsid) - - # Wrap all non-pPr children in - del_wrapper = self.dom.createElement("w:del") - for child in [c for c in elem.childNodes if c.nodeName != "w:pPr"]: - elem.removeChild(child) - del_wrapper.appendChild(child) - elem.appendChild(del_wrapper) - - # Inject attributes to the deletion wrapper - self._inject_attributes_to_nodes([del_wrapper]) - - return elem - - else: - raise ValueError(f"Element must be w:r or w:p, got {elem.nodeName}") - - -def _generate_hex_id() -> str: - """Generate random 8-character hex ID for para/durable IDs. - - Values are constrained to be less than 0x7FFFFFFF per OOXML spec: - - paraId must be < 0x80000000 - - durableId must be < 0x7FFFFFFF - We use the stricter constraint (0x7FFFFFFF) for both. - """ - return f"{random.randint(1, 0x7FFFFFFE):08X}" - - -def _generate_rsid() -> str: - """Generate random 8-character hex RSID.""" - return "".join(random.choices("0123456789ABCDEF", k=8)) - - -class Document: - """Manages comments in unpacked Word documents.""" - - def __init__( - self, - unpacked_dir, - rsid=None, - track_revisions=False, - author="Claude", - initials="C", - ): - """ - Initialize with path to unpacked Word document directory. - Automatically sets up comment infrastructure (people.xml, RSIDs). - - Args: - unpacked_dir: Path to unpacked DOCX directory (must contain word/ subdirectory) - rsid: Optional RSID to use for all comment elements. If not provided, one will be generated. - track_revisions: If True, enables track revisions in settings.xml (default: False) - author: Default author name for comments (default: "Claude") - initials: Default author initials for comments (default: "C") - """ - self.original_path = Path(unpacked_dir) - - if not self.original_path.exists() or not self.original_path.is_dir(): - raise ValueError(f"Directory not found: {unpacked_dir}") - - # Create temporary directory with subdirectories for unpacked content and baseline - self.temp_dir = tempfile.mkdtemp(prefix="docx_") - self.unpacked_path = Path(self.temp_dir) / "unpacked" - shutil.copytree(self.original_path, self.unpacked_path) - - # Pack original directory into temporary .docx for validation baseline (outside unpacked dir) - self.original_docx = Path(self.temp_dir) / "original.docx" - pack_document(self.original_path, self.original_docx, validate=False) - - self.word_path = self.unpacked_path / "word" - - # Generate RSID if not provided - self.rsid = rsid if rsid else _generate_rsid() - print(f"Using RSID: {self.rsid}") - - # Set default author and initials - self.author = author - self.initials = initials - - # Cache for lazy-loaded editors - self._editors = {} - - # Comment file paths - self.comments_path = self.word_path / "comments.xml" - self.comments_extended_path = self.word_path / "commentsExtended.xml" - self.comments_ids_path = self.word_path / "commentsIds.xml" - self.comments_extensible_path = self.word_path / "commentsExtensible.xml" - - # Load existing comments and determine next ID (before setup modifies files) - self.existing_comments = self._load_existing_comments() - self.next_comment_id = self._get_next_comment_id() - - # Convenient access to document.xml editor (semi-private) - self._document = self["word/document.xml"] - - # Setup tracked changes infrastructure - self._setup_tracking(track_revisions=track_revisions) - - # Add author to people.xml - self._add_author_to_people(author) - - def __getitem__(self, xml_path: str) -> DocxXMLEditor: - """ - Get or create a DocxXMLEditor for the specified XML file. - - Enables lazy-loaded editors with bracket notation: - node = doc["word/document.xml"].get_node(tag="w:p", line_number=42) - - Args: - xml_path: Relative path to XML file (e.g., "word/document.xml", "word/comments.xml") - - Returns: - DocxXMLEditor instance for the specified file - - Raises: - ValueError: If the file does not exist - - Example: - # Get node from document.xml - node = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "1"}) - - # Get node from comments.xml - comment = doc["word/comments.xml"].get_node(tag="w:comment", attrs={"w:id": "0"}) - """ - if xml_path not in self._editors: - file_path = self.unpacked_path / xml_path - if not file_path.exists(): - raise ValueError(f"XML file not found: {xml_path}") - # Use DocxXMLEditor with RSID, author, and initials for all editors - self._editors[xml_path] = DocxXMLEditor( - file_path, rsid=self.rsid, author=self.author, initials=self.initials - ) - return self._editors[xml_path] - - def add_comment(self, start, end, text: str) -> int: - """ - Add a comment spanning from one element to another. - - Args: - start: DOM element for the starting point - end: DOM element for the ending point - text: Comment content - - Returns: - The comment ID that was created - - Example: - start_node = cm.get_document_node(tag="w:del", id="1") - end_node = cm.get_document_node(tag="w:ins", id="2") - cm.add_comment(start=start_node, end=end_node, text="Explanation") - """ - comment_id = self.next_comment_id - para_id = _generate_hex_id() - durable_id = _generate_hex_id() - timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - # Add comment ranges to document.xml immediately - self._document.insert_before(start, self._comment_range_start_xml(comment_id)) - - # If end node is a paragraph, append comment markup inside it - # Otherwise insert after it (for run-level anchors) - if end.tagName == "w:p": - self._document.append_to(end, self._comment_range_end_xml(comment_id)) - else: - self._document.insert_after(end, self._comment_range_end_xml(comment_id)) - - # Add to comments.xml immediately - self._add_to_comments_xml( - comment_id, para_id, text, self.author, self.initials, timestamp - ) - - # Add to commentsExtended.xml immediately - self._add_to_comments_extended_xml(para_id, parent_para_id=None) - - # Add to commentsIds.xml immediately - self._add_to_comments_ids_xml(para_id, durable_id) - - # Add to commentsExtensible.xml immediately - self._add_to_comments_extensible_xml(durable_id) - - # Update existing_comments so replies work - self.existing_comments[comment_id] = {"para_id": para_id} - - self.next_comment_id += 1 - return comment_id - - def reply_to_comment( - self, - parent_comment_id: int, - text: str, - ) -> int: - """ - Add a reply to an existing comment. - - Args: - parent_comment_id: The w:id of the parent comment to reply to - text: Reply text - - Returns: - The comment ID that was created for the reply - - Example: - cm.reply_to_comment(parent_comment_id=0, text="I agree with this change") - """ - if parent_comment_id not in self.existing_comments: - raise ValueError(f"Parent comment with id={parent_comment_id} not found") - - parent_info = self.existing_comments[parent_comment_id] - comment_id = self.next_comment_id - para_id = _generate_hex_id() - durable_id = _generate_hex_id() - timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - # Add comment ranges to document.xml immediately - parent_start_elem = self._document.get_node( - tag="w:commentRangeStart", attrs={"w:id": str(parent_comment_id)} - ) - parent_ref_elem = self._document.get_node( - tag="w:commentReference", attrs={"w:id": str(parent_comment_id)} - ) - - self._document.insert_after( - parent_start_elem, self._comment_range_start_xml(comment_id) - ) - parent_ref_run = parent_ref_elem.parentNode - self._document.insert_after( - parent_ref_run, f'' - ) - self._document.insert_after( - parent_ref_run, self._comment_ref_run_xml(comment_id) - ) - - # Add to comments.xml immediately - self._add_to_comments_xml( - comment_id, para_id, text, self.author, self.initials, timestamp - ) - - # Add to commentsExtended.xml immediately (with parent) - self._add_to_comments_extended_xml( - para_id, parent_para_id=parent_info["para_id"] - ) - - # Add to commentsIds.xml immediately - self._add_to_comments_ids_xml(para_id, durable_id) - - # Add to commentsExtensible.xml immediately - self._add_to_comments_extensible_xml(durable_id) - - # Update existing_comments so replies work - self.existing_comments[comment_id] = {"para_id": para_id} - - self.next_comment_id += 1 - return comment_id - - def __del__(self): - """Clean up temporary directory on deletion.""" - if hasattr(self, "temp_dir") and Path(self.temp_dir).exists(): - shutil.rmtree(self.temp_dir) - - def validate(self) -> None: - """ - Validate the document against XSD schema and redlining rules. - - Raises: - ValueError: If validation fails. - """ - # Create validators with current state - schema_validator = DOCXSchemaValidator( - self.unpacked_path, self.original_docx, verbose=False - ) - redlining_validator = RedliningValidator( - self.unpacked_path, self.original_docx, verbose=False - ) - - # Run validations - if not schema_validator.validate(): - raise ValueError("Schema validation failed") - if not redlining_validator.validate(): - raise ValueError("Redlining validation failed") - - def save(self, destination=None, validate=True) -> None: - """ - Save all modified XML files to disk and copy to destination directory. - - This persists all changes made via add_comment() and reply_to_comment(). - - Args: - destination: Optional path to save to. If None, saves back to original directory. - validate: If True, validates document before saving (default: True). - """ - # Only ensure comment relationships and content types if comment files exist - if self.comments_path.exists(): - self._ensure_comment_relationships() - self._ensure_comment_content_types() - - # Save all modified XML files in temp directory - for editor in self._editors.values(): - editor.save() - - # Validate by default - if validate: - self.validate() - - # Copy contents from temp directory to destination (or original directory) - target_path = Path(destination) if destination else self.original_path - shutil.copytree(self.unpacked_path, target_path, dirs_exist_ok=True) - - # ==================== Private: Initialization ==================== - - def _get_next_comment_id(self): - """Get the next available comment ID.""" - if not self.comments_path.exists(): - return 0 - - editor = self["word/comments.xml"] - max_id = -1 - for comment_elem in editor.dom.getElementsByTagName("w:comment"): - comment_id = comment_elem.getAttribute("w:id") - if comment_id: - try: - max_id = max(max_id, int(comment_id)) - except ValueError: - pass - return max_id + 1 - - def _load_existing_comments(self): - """Load existing comments from files to enable replies.""" - if not self.comments_path.exists(): - return {} - - editor = self["word/comments.xml"] - existing = {} - - for comment_elem in editor.dom.getElementsByTagName("w:comment"): - comment_id = comment_elem.getAttribute("w:id") - if not comment_id: - continue - - # Find para_id from the w:p element within the comment - para_id = None - for p_elem in comment_elem.getElementsByTagName("w:p"): - para_id = p_elem.getAttribute("w14:paraId") - if para_id: - break - - if not para_id: - continue - - existing[int(comment_id)] = {"para_id": para_id} - - return existing - - # ==================== Private: Setup Methods ==================== - - def _setup_tracking(self, track_revisions=False): - """Set up comment infrastructure in unpacked directory. - - Args: - track_revisions: If True, enables track revisions in settings.xml - """ - # Create or update word/people.xml - people_file = self.word_path / "people.xml" - self._update_people_xml(people_file) - - # Update XML files - self._add_content_type_for_people(self.unpacked_path / "[Content_Types].xml") - self._add_relationship_for_people( - self.word_path / "_rels" / "document.xml.rels" - ) - - # Always add RSID to settings.xml, optionally enable trackRevisions - self._update_settings( - self.word_path / "settings.xml", track_revisions=track_revisions - ) - - def _update_people_xml(self, path): - """Create people.xml if it doesn't exist.""" - if not path.exists(): - # Copy from template - shutil.copy(TEMPLATE_DIR / "people.xml", path) - - def _add_content_type_for_people(self, path): - """Add people.xml content type to [Content_Types].xml if not already present.""" - editor = self["[Content_Types].xml"] - - if self._has_override(editor, "/word/people.xml"): - return - - # Add Override element - root = editor.dom.documentElement - override_xml = '' - editor.append_to(root, override_xml) - - def _add_relationship_for_people(self, path): - """Add people.xml relationship to document.xml.rels if not already present.""" - editor = self["word/_rels/document.xml.rels"] - - if self._has_relationship(editor, "people.xml"): - return - - root = editor.dom.documentElement - root_tag = root.tagName # type: ignore - prefix = root_tag.split(":")[0] + ":" if ":" in root_tag else "" - next_rid = editor.get_next_rid() - - # Create the relationship entry - rel_xml = f'<{prefix}Relationship Id="{next_rid}" Type="http://schemas.microsoft.com/office/2011/relationships/people" Target="people.xml"/>' - editor.append_to(root, rel_xml) - - def _update_settings(self, path, track_revisions=False): - """Add RSID and optionally enable track revisions in settings.xml. - - Args: - path: Path to settings.xml - track_revisions: If True, adds trackRevisions element - - Places elements per OOXML schema order: - - trackRevisions: early (before defaultTabStop) - - rsids: late (after compat) - """ - editor = self["word/settings.xml"] - root = editor.get_node(tag="w:settings") - prefix = root.tagName.split(":")[0] if ":" in root.tagName else "w" - - # Conditionally add trackRevisions if requested - if track_revisions: - track_revisions_exists = any( - elem.tagName == f"{prefix}:trackRevisions" - for elem in editor.dom.getElementsByTagName(f"{prefix}:trackRevisions") - ) - - if not track_revisions_exists: - track_rev_xml = f"<{prefix}:trackRevisions/>" - # Try to insert before documentProtection, defaultTabStop, or at start - inserted = False - for tag in [f"{prefix}:documentProtection", f"{prefix}:defaultTabStop"]: - elements = editor.dom.getElementsByTagName(tag) - if elements: - editor.insert_before(elements[0], track_rev_xml) - inserted = True - break - if not inserted: - # Insert as first child of settings - if root.firstChild: - editor.insert_before(root.firstChild, track_rev_xml) - else: - editor.append_to(root, track_rev_xml) - - # Always check if rsids section exists - rsids_elements = editor.dom.getElementsByTagName(f"{prefix}:rsids") - - if not rsids_elements: - # Add new rsids section - rsids_xml = f'''<{prefix}:rsids> - <{prefix}:rsidRoot {prefix}:val="{self.rsid}"/> - <{prefix}:rsid {prefix}:val="{self.rsid}"/> -''' - - # Try to insert after compat, before clrSchemeMapping, or before closing tag - inserted = False - compat_elements = editor.dom.getElementsByTagName(f"{prefix}:compat") - if compat_elements: - editor.insert_after(compat_elements[0], rsids_xml) - inserted = True - - if not inserted: - clr_elements = editor.dom.getElementsByTagName( - f"{prefix}:clrSchemeMapping" - ) - if clr_elements: - editor.insert_before(clr_elements[0], rsids_xml) - inserted = True - - if not inserted: - editor.append_to(root, rsids_xml) - else: - # Check if this rsid already exists - rsids_elem = rsids_elements[0] - rsid_exists = any( - elem.getAttribute(f"{prefix}:val") == self.rsid - for elem in rsids_elem.getElementsByTagName(f"{prefix}:rsid") - ) - - if not rsid_exists: - rsid_xml = f'<{prefix}:rsid {prefix}:val="{self.rsid}"/>' - editor.append_to(rsids_elem, rsid_xml) - - # ==================== Private: XML File Creation ==================== - - def _add_to_comments_xml( - self, comment_id, para_id, text, author, initials, timestamp - ): - """Add a single comment to comments.xml.""" - if not self.comments_path.exists(): - shutil.copy(TEMPLATE_DIR / "comments.xml", self.comments_path) - - editor = self["word/comments.xml"] - root = editor.get_node(tag="w:comments") - - escaped_text = ( - text.replace("&", "&").replace("<", "<").replace(">", ">") - ) - # Note: w:rsidR, w:rsidRDefault, w:rsidP on w:p, w:rsidR on w:r, - # and w:author, w:date, w:initials on w:comment are automatically added by DocxXMLEditor - comment_xml = f''' - - - {escaped_text} - -''' - editor.append_to(root, comment_xml) - - def _add_to_comments_extended_xml(self, para_id, parent_para_id): - """Add a single comment to commentsExtended.xml.""" - if not self.comments_extended_path.exists(): - shutil.copy( - TEMPLATE_DIR / "commentsExtended.xml", self.comments_extended_path - ) - - editor = self["word/commentsExtended.xml"] - root = editor.get_node(tag="w15:commentsEx") - - if parent_para_id: - xml = f'' - else: - xml = f'' - editor.append_to(root, xml) - - def _add_to_comments_ids_xml(self, para_id, durable_id): - """Add a single comment to commentsIds.xml.""" - if not self.comments_ids_path.exists(): - shutil.copy(TEMPLATE_DIR / "commentsIds.xml", self.comments_ids_path) - - editor = self["word/commentsIds.xml"] - root = editor.get_node(tag="w16cid:commentsIds") - - xml = f'' - editor.append_to(root, xml) - - def _add_to_comments_extensible_xml(self, durable_id): - """Add a single comment to commentsExtensible.xml.""" - if not self.comments_extensible_path.exists(): - shutil.copy( - TEMPLATE_DIR / "commentsExtensible.xml", self.comments_extensible_path - ) - - editor = self["word/commentsExtensible.xml"] - root = editor.get_node(tag="w16cex:commentsExtensible") - - xml = f'' - editor.append_to(root, xml) - - # ==================== Private: XML Fragments ==================== - - def _comment_range_start_xml(self, comment_id): - """Generate XML for comment range start.""" - return f'' - - def _comment_range_end_xml(self, comment_id): - """Generate XML for comment range end with reference run. - - Note: w:rsidR is automatically added by DocxXMLEditor. - """ - return f''' - - - -''' - - def _comment_ref_run_xml(self, comment_id): - """Generate XML for comment reference run. - - Note: w:rsidR is automatically added by DocxXMLEditor. - """ - return f''' - - -''' - - # ==================== Private: Metadata Updates ==================== - - def _has_relationship(self, editor, target): - """Check if a relationship with given target exists.""" - for rel_elem in editor.dom.getElementsByTagName("Relationship"): - if rel_elem.getAttribute("Target") == target: - return True - return False - - def _has_override(self, editor, part_name): - """Check if an override with given part name exists.""" - for override_elem in editor.dom.getElementsByTagName("Override"): - if override_elem.getAttribute("PartName") == part_name: - return True - return False - - def _has_author(self, editor, author): - """Check if an author already exists in people.xml.""" - for person_elem in editor.dom.getElementsByTagName("w15:person"): - if person_elem.getAttribute("w15:author") == author: - return True - return False - - def _add_author_to_people(self, author): - """Add author to people.xml (called during initialization).""" - people_path = self.word_path / "people.xml" - - # people.xml should already exist from _setup_tracking - if not people_path.exists(): - raise ValueError("people.xml should exist after _setup_tracking") - - editor = self["word/people.xml"] - root = editor.get_node(tag="w15:people") - - # Check if author already exists - if self._has_author(editor, author): - return - - # Add author with proper XML escaping to prevent injection - escaped_author = html.escape(author, quote=True) - person_xml = f''' - -''' - editor.append_to(root, person_xml) - - def _ensure_comment_relationships(self): - """Ensure word/_rels/document.xml.rels has comment relationships.""" - editor = self["word/_rels/document.xml.rels"] - - if self._has_relationship(editor, "comments.xml"): - return - - root = editor.dom.documentElement - root_tag = root.tagName # type: ignore - prefix = root_tag.split(":")[0] + ":" if ":" in root_tag else "" - next_rid_num = int(editor.get_next_rid()[3:]) - - # Add relationship elements - rels = [ - ( - next_rid_num, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", - "comments.xml", - ), - ( - next_rid_num + 1, - "http://schemas.microsoft.com/office/2011/relationships/commentsExtended", - "commentsExtended.xml", - ), - ( - next_rid_num + 2, - "http://schemas.microsoft.com/office/2016/09/relationships/commentsIds", - "commentsIds.xml", - ), - ( - next_rid_num + 3, - "http://schemas.microsoft.com/office/2018/08/relationships/commentsExtensible", - "commentsExtensible.xml", - ), - ] - - for rel_id, rel_type, target in rels: - rel_xml = f'<{prefix}Relationship Id="rId{rel_id}" Type="{rel_type}" Target="{target}"/>' - editor.append_to(root, rel_xml) - - def _ensure_comment_content_types(self): - """Ensure [Content_Types].xml has comment content types.""" - editor = self["[Content_Types].xml"] - - if self._has_override(editor, "/word/comments.xml"): - return - - root = editor.dom.documentElement - - # Add Override elements - overrides = [ - ( - "/word/comments.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", - ), - ( - "/word/commentsExtended.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml", - ), - ( - "/word/commentsIds.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml", - ), - ( - "/word/commentsExtensible.xml", - "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml", - ), - ] - - for part_name, content_type in overrides: - override_xml = ( - f'' - ) - editor.append_to(root, override_xml) diff --git a/document-skills/docx/scripts/templates/comments.xml b/document-skills/docx/scripts/templates/comments.xml deleted file mode 100644 index b5dace0ef..000000000 --- a/document-skills/docx/scripts/templates/comments.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/document-skills/docx/scripts/templates/commentsExtended.xml b/document-skills/docx/scripts/templates/commentsExtended.xml deleted file mode 100644 index b4cf23e35..000000000 --- a/document-skills/docx/scripts/templates/commentsExtended.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/document-skills/docx/scripts/templates/commentsExtensible.xml b/document-skills/docx/scripts/templates/commentsExtensible.xml deleted file mode 100644 index e32a05e0c..000000000 --- a/document-skills/docx/scripts/templates/commentsExtensible.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/document-skills/docx/scripts/templates/commentsIds.xml b/document-skills/docx/scripts/templates/commentsIds.xml deleted file mode 100644 index d04bc8e06..000000000 --- a/document-skills/docx/scripts/templates/commentsIds.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/document-skills/docx/scripts/templates/people.xml b/document-skills/docx/scripts/templates/people.xml deleted file mode 100644 index a839cafeb..000000000 --- a/document-skills/docx/scripts/templates/people.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/document-skills/docx/scripts/utilities.py b/document-skills/docx/scripts/utilities.py deleted file mode 100755 index d92dae611..000000000 --- a/document-skills/docx/scripts/utilities.py +++ /dev/null @@ -1,374 +0,0 @@ -#!/usr/bin/env python3 -""" -Utilities for editing OOXML documents. - -This module provides XMLEditor, a tool for manipulating XML files with support for -line-number-based node finding and DOM manipulation. Each element is automatically -annotated with its original line and column position during parsing. - -Example usage: - editor = XMLEditor("document.xml") - - # Find node by line number or range - elem = editor.get_node(tag="w:r", line_number=519) - elem = editor.get_node(tag="w:p", line_number=range(100, 200)) - - # Find node by text content - elem = editor.get_node(tag="w:p", contains="specific text") - - # Find node by attributes - elem = editor.get_node(tag="w:r", attrs={"w:id": "target"}) - - # Combine filters - elem = editor.get_node(tag="w:p", line_number=range(1, 50), contains="text") - - # Replace, insert, or manipulate - new_elem = editor.replace_node(elem, "new text") - editor.insert_after(new_elem, "more") - - # Save changes - editor.save() -""" - -import html -from pathlib import Path -from typing import Optional, Union - -import defusedxml.minidom -import defusedxml.sax - - -class XMLEditor: - """ - Editor for manipulating OOXML XML files with line-number-based node finding. - - This class parses XML files and tracks the original line and column position - of each element. This enables finding nodes by their line number in the original - file, which is useful when working with Read tool output. - - Attributes: - xml_path: Path to the XML file being edited - encoding: Detected encoding of the XML file ('ascii' or 'utf-8') - dom: Parsed DOM tree with parse_position attributes on elements - """ - - def __init__(self, xml_path): - """ - Initialize with path to XML file and parse with line number tracking. - - Args: - xml_path: Path to XML file to edit (str or Path) - - Raises: - ValueError: If the XML file does not exist - """ - self.xml_path = Path(xml_path) - if not self.xml_path.exists(): - raise ValueError(f"XML file not found: {xml_path}") - - with open(self.xml_path, "rb") as f: - header = f.read(200).decode("utf-8", errors="ignore") - self.encoding = "ascii" if 'encoding="ascii"' in header else "utf-8" - - parser = _create_line_tracking_parser() - self.dom = defusedxml.minidom.parse(str(self.xml_path), parser) - - def get_node( - self, - tag: str, - attrs: Optional[dict[str, str]] = None, - line_number: Optional[Union[int, range]] = None, - contains: Optional[str] = None, - ): - """ - Get a DOM element by tag and identifier. - - Finds an element by either its line number in the original file or by - matching attribute values. Exactly one match must be found. - - Args: - tag: The XML tag name (e.g., "w:del", "w:ins", "w:r") - attrs: Dictionary of attribute name-value pairs to match (e.g., {"w:id": "1"}) - line_number: Line number (int) or line range (range) in original XML file (1-indexed) - contains: Text string that must appear in any text node within the element. - Supports both entity notation (“) and Unicode characters (\u201c). - - Returns: - defusedxml.minidom.Element: The matching DOM element - - Raises: - ValueError: If node not found or multiple matches found - - Example: - elem = editor.get_node(tag="w:r", line_number=519) - elem = editor.get_node(tag="w:r", line_number=range(100, 200)) - elem = editor.get_node(tag="w:del", attrs={"w:id": "1"}) - elem = editor.get_node(tag="w:p", attrs={"w14:paraId": "12345678"}) - elem = editor.get_node(tag="w:commentRangeStart", attrs={"w:id": "0"}) - elem = editor.get_node(tag="w:p", contains="specific text") - elem = editor.get_node(tag="w:t", contains="“Agreement") # Entity notation - elem = editor.get_node(tag="w:t", contains="\u201cAgreement") # Unicode character - """ - matches = [] - for elem in self.dom.getElementsByTagName(tag): - # Check line_number filter - if line_number is not None: - parse_pos = getattr(elem, "parse_position", (None,)) - elem_line = parse_pos[0] - - # Handle both single line number and range - if isinstance(line_number, range): - if elem_line not in line_number: - continue - else: - if elem_line != line_number: - continue - - # Check attrs filter - if attrs is not None: - if not all( - elem.getAttribute(attr_name) == attr_value - for attr_name, attr_value in attrs.items() - ): - continue - - # Check contains filter - if contains is not None: - elem_text = self._get_element_text(elem) - # Normalize the search string: convert HTML entities to Unicode characters - # This allows searching for both "“Rowan" and ""Rowan" - normalized_contains = html.unescape(contains) - if normalized_contains not in elem_text: - continue - - # If all applicable filters passed, this is a match - matches.append(elem) - - if not matches: - # Build descriptive error message - filters = [] - if line_number is not None: - line_str = ( - f"lines {line_number.start}-{line_number.stop - 1}" - if isinstance(line_number, range) - else f"line {line_number}" - ) - filters.append(f"at {line_str}") - if attrs is not None: - filters.append(f"with attributes {attrs}") - if contains is not None: - filters.append(f"containing '{contains}'") - - filter_desc = " ".join(filters) if filters else "" - base_msg = f"Node not found: <{tag}> {filter_desc}".strip() - - # Add helpful hint based on filters used - if contains: - hint = "Text may be split across elements or use different wording." - elif line_number: - hint = "Line numbers may have changed if document was modified." - elif attrs: - hint = "Verify attribute values are correct." - else: - hint = "Try adding filters (attrs, line_number, or contains)." - - raise ValueError(f"{base_msg}. {hint}") - if len(matches) > 1: - raise ValueError( - f"Multiple nodes found: <{tag}>. " - f"Add more filters (attrs, line_number, or contains) to narrow the search." - ) - return matches[0] - - def _get_element_text(self, elem): - """ - Recursively extract all text content from an element. - - Skips text nodes that contain only whitespace (spaces, tabs, newlines), - which typically represent XML formatting rather than document content. - - Args: - elem: defusedxml.minidom.Element to extract text from - - Returns: - str: Concatenated text from all non-whitespace text nodes within the element - """ - text_parts = [] - for node in elem.childNodes: - if node.nodeType == node.TEXT_NODE: - # Skip whitespace-only text nodes (XML formatting) - if node.data.strip(): - text_parts.append(node.data) - elif node.nodeType == node.ELEMENT_NODE: - text_parts.append(self._get_element_text(node)) - return "".join(text_parts) - - def replace_node(self, elem, new_content): - """ - Replace a DOM element with new XML content. - - Args: - elem: defusedxml.minidom.Element to replace - new_content: String containing XML to replace the node with - - Returns: - List[defusedxml.minidom.Node]: All inserted nodes - - Example: - new_nodes = editor.replace_node(old_elem, "text") - """ - parent = elem.parentNode - nodes = self._parse_fragment(new_content) - for node in nodes: - parent.insertBefore(node, elem) - parent.removeChild(elem) - return nodes - - def insert_after(self, elem, xml_content): - """ - Insert XML content after a DOM element. - - Args: - elem: defusedxml.minidom.Element to insert after - xml_content: String containing XML to insert - - Returns: - List[defusedxml.minidom.Node]: All inserted nodes - - Example: - new_nodes = editor.insert_after(elem, "text") - """ - parent = elem.parentNode - next_sibling = elem.nextSibling - nodes = self._parse_fragment(xml_content) - for node in nodes: - if next_sibling: - parent.insertBefore(node, next_sibling) - else: - parent.appendChild(node) - return nodes - - def insert_before(self, elem, xml_content): - """ - Insert XML content before a DOM element. - - Args: - elem: defusedxml.minidom.Element to insert before - xml_content: String containing XML to insert - - Returns: - List[defusedxml.minidom.Node]: All inserted nodes - - Example: - new_nodes = editor.insert_before(elem, "text") - """ - parent = elem.parentNode - nodes = self._parse_fragment(xml_content) - for node in nodes: - parent.insertBefore(node, elem) - return nodes - - def append_to(self, elem, xml_content): - """ - Append XML content as a child of a DOM element. - - Args: - elem: defusedxml.minidom.Element to append to - xml_content: String containing XML to append - - Returns: - List[defusedxml.minidom.Node]: All inserted nodes - - Example: - new_nodes = editor.append_to(elem, "text") - """ - nodes = self._parse_fragment(xml_content) - for node in nodes: - elem.appendChild(node) - return nodes - - def get_next_rid(self): - """Get the next available rId for relationships files.""" - max_id = 0 - for rel_elem in self.dom.getElementsByTagName("Relationship"): - rel_id = rel_elem.getAttribute("Id") - if rel_id.startswith("rId"): - try: - max_id = max(max_id, int(rel_id[3:])) - except ValueError: - pass - return f"rId{max_id + 1}" - - def save(self): - """ - Save the edited XML back to the file. - - Serializes the DOM tree and writes it back to the original file path, - preserving the original encoding (ascii or utf-8). - """ - content = self.dom.toxml(encoding=self.encoding) - self.xml_path.write_bytes(content) - - def _parse_fragment(self, xml_content): - """ - Parse XML fragment and return list of imported nodes. - - Args: - xml_content: String containing XML fragment - - Returns: - List of defusedxml.minidom.Node objects imported into this document - - Raises: - AssertionError: If fragment contains no element nodes - """ - # Extract namespace declarations from the root document element - root_elem = self.dom.documentElement - namespaces = [] - if root_elem and root_elem.attributes: - for i in range(root_elem.attributes.length): - attr = root_elem.attributes.item(i) - if attr.name.startswith("xmlns"): # type: ignore - namespaces.append(f'{attr.name}="{attr.value}"') # type: ignore - - ns_decl = " ".join(namespaces) - wrapper = f"{xml_content}" - fragment_doc = defusedxml.minidom.parseString(wrapper) - nodes = [ - self.dom.importNode(child, deep=True) - for child in fragment_doc.documentElement.childNodes # type: ignore - ] - elements = [n for n in nodes if n.nodeType == n.ELEMENT_NODE] - assert elements, "Fragment must contain at least one element" - return nodes - - -def _create_line_tracking_parser(): - """ - Create a SAX parser that tracks line and column numbers for each element. - - Monkey patches the SAX content handler to store the current line and column - position from the underlying expat parser onto each element as a parse_position - attribute (line, column) tuple. - - Returns: - defusedxml.sax.xmlreader.XMLReader: Configured SAX parser - """ - - def set_content_handler(dom_handler): - def startElementNS(name, tagName, attrs): - orig_start_cb(name, tagName, attrs) - cur_elem = dom_handler.elementStack[-1] - cur_elem.parse_position = ( - parser._parser.CurrentLineNumber, # type: ignore - parser._parser.CurrentColumnNumber, # type: ignore - ) - - orig_start_cb = dom_handler.startElementNS - dom_handler.startElementNS = startElementNS - orig_set_content_handler(dom_handler) - - parser = defusedxml.sax.make_parser() - orig_set_content_handler = parser.setContentHandler - parser.setContentHandler = set_content_handler # type: ignore - return parser diff --git a/document-skills/pdf/LICENSE.txt b/document-skills/pdf/LICENSE.txt deleted file mode 100644 index c55ab4222..000000000 --- a/document-skills/pdf/LICENSE.txt +++ /dev/null @@ -1,30 +0,0 @@ -© 2025 Anthropic, PBC. All rights reserved. - -LICENSE: Use of these materials (including all code, prompts, assets, files, -and other components of this Skill) is governed by your agreement with -Anthropic regarding use of Anthropic's services. If no separate agreement -exists, use is governed by Anthropic's Consumer Terms of Service or -Commercial Terms of Service, as applicable: -https://www.anthropic.com/legal/consumer-terms -https://www.anthropic.com/legal/commercial-terms -Your applicable agreement is referred to as the "Agreement." "Services" are -as defined in the Agreement. - -ADDITIONAL RESTRICTIONS: Notwithstanding anything in the Agreement to the -contrary, users may not: - -- Extract these materials from the Services or retain copies of these - materials outside the Services -- Reproduce or copy these materials, except for temporary copies created - automatically during authorized use of the Services -- Create derivative works based on these materials -- Distribute, sublicense, or transfer these materials to any third party -- Make, offer to sell, sell, or import any inventions embodied in these - materials -- Reverse engineer, decompile, or disassemble these materials - -The receipt, viewing, or possession of these materials does not convey or -imply any license or right beyond those expressly granted above. - -Anthropic retains all right, title, and interest in these materials, -including all copyrights, patents, and other intellectual property rights. diff --git a/document-skills/pdf/SKILL.md b/document-skills/pdf/SKILL.md deleted file mode 100644 index f6a22ddf8..000000000 --- a/document-skills/pdf/SKILL.md +++ /dev/null @@ -1,294 +0,0 @@ ---- -name: pdf -description: Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. When Claude needs to fill in a PDF form or programmatically process, generate, or analyze PDF documents at scale. -license: Proprietary. LICENSE.txt has complete terms ---- - -# PDF Processing Guide - -## Overview - -This guide covers essential PDF processing operations using Python libraries and command-line tools. For advanced features, JavaScript libraries, and detailed examples, see reference.md. If you need to fill out a PDF form, read forms.md and follow its instructions. - -## Quick Start - -```python -from pypdf import PdfReader, PdfWriter - -# Read a PDF -reader = PdfReader("document.pdf") -print(f"Pages: {len(reader.pages)}") - -# Extract text -text = "" -for page in reader.pages: - text += page.extract_text() -``` - -## Python Libraries - -### pypdf - Basic Operations - -#### Merge PDFs -```python -from pypdf import PdfWriter, PdfReader - -writer = PdfWriter() -for pdf_file in ["doc1.pdf", "doc2.pdf", "doc3.pdf"]: - reader = PdfReader(pdf_file) - for page in reader.pages: - writer.add_page(page) - -with open("merged.pdf", "wb") as output: - writer.write(output) -``` - -#### Split PDF -```python -reader = PdfReader("input.pdf") -for i, page in enumerate(reader.pages): - writer = PdfWriter() - writer.add_page(page) - with open(f"page_{i+1}.pdf", "wb") as output: - writer.write(output) -``` - -#### Extract Metadata -```python -reader = PdfReader("document.pdf") -meta = reader.metadata -print(f"Title: {meta.title}") -print(f"Author: {meta.author}") -print(f"Subject: {meta.subject}") -print(f"Creator: {meta.creator}") -``` - -#### Rotate Pages -```python -reader = PdfReader("input.pdf") -writer = PdfWriter() - -page = reader.pages[0] -page.rotate(90) # Rotate 90 degrees clockwise -writer.add_page(page) - -with open("rotated.pdf", "wb") as output: - writer.write(output) -``` - -### pdfplumber - Text and Table Extraction - -#### Extract Text with Layout -```python -import pdfplumber - -with pdfplumber.open("document.pdf") as pdf: - for page in pdf.pages: - text = page.extract_text() - print(text) -``` - -#### Extract Tables -```python -with pdfplumber.open("document.pdf") as pdf: - for i, page in enumerate(pdf.pages): - tables = page.extract_tables() - for j, table in enumerate(tables): - print(f"Table {j+1} on page {i+1}:") - for row in table: - print(row) -``` - -#### Advanced Table Extraction -```python -import pandas as pd - -with pdfplumber.open("document.pdf") as pdf: - all_tables = [] - for page in pdf.pages: - tables = page.extract_tables() - for table in tables: - if table: # Check if table is not empty - df = pd.DataFrame(table[1:], columns=table[0]) - all_tables.append(df) - -# Combine all tables -if all_tables: - combined_df = pd.concat(all_tables, ignore_index=True) - combined_df.to_excel("extracted_tables.xlsx", index=False) -``` - -### reportlab - Create PDFs - -#### Basic PDF Creation -```python -from reportlab.lib.pagesizes import letter -from reportlab.pdfgen import canvas - -c = canvas.Canvas("hello.pdf", pagesize=letter) -width, height = letter - -# Add text -c.drawString(100, height - 100, "Hello World!") -c.drawString(100, height - 120, "This is a PDF created with reportlab") - -# Add a line -c.line(100, height - 140, 400, height - 140) - -# Save -c.save() -``` - -#### Create PDF with Multiple Pages -```python -from reportlab.lib.pagesizes import letter -from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak -from reportlab.lib.styles import getSampleStyleSheet - -doc = SimpleDocTemplate("report.pdf", pagesize=letter) -styles = getSampleStyleSheet() -story = [] - -# Add content -title = Paragraph("Report Title", styles['Title']) -story.append(title) -story.append(Spacer(1, 12)) - -body = Paragraph("This is the body of the report. " * 20, styles['Normal']) -story.append(body) -story.append(PageBreak()) - -# Page 2 -story.append(Paragraph("Page 2", styles['Heading1'])) -story.append(Paragraph("Content for page 2", styles['Normal'])) - -# Build PDF -doc.build(story) -``` - -## Command-Line Tools - -### pdftotext (poppler-utils) -```bash -# Extract text -pdftotext input.pdf output.txt - -# Extract text preserving layout -pdftotext -layout input.pdf output.txt - -# Extract specific pages -pdftotext -f 1 -l 5 input.pdf output.txt # Pages 1-5 -``` - -### qpdf -```bash -# Merge PDFs -qpdf --empty --pages file1.pdf file2.pdf -- merged.pdf - -# Split pages -qpdf input.pdf --pages . 1-5 -- pages1-5.pdf -qpdf input.pdf --pages . 6-10 -- pages6-10.pdf - -# Rotate pages -qpdf input.pdf output.pdf --rotate=+90:1 # Rotate page 1 by 90 degrees - -# Remove password -qpdf --password=mypassword --decrypt encrypted.pdf decrypted.pdf -``` - -### pdftk (if available) -```bash -# Merge -pdftk file1.pdf file2.pdf cat output merged.pdf - -# Split -pdftk input.pdf burst - -# Rotate -pdftk input.pdf rotate 1east output rotated.pdf -``` - -## Common Tasks - -### Extract Text from Scanned PDFs -```python -# Requires: pip install pytesseract pdf2image -import pytesseract -from pdf2image import convert_from_path - -# Convert PDF to images -images = convert_from_path('scanned.pdf') - -# OCR each page -text = "" -for i, image in enumerate(images): - text += f"Page {i+1}:\n" - text += pytesseract.image_to_string(image) - text += "\n\n" - -print(text) -``` - -### Add Watermark -```python -from pypdf import PdfReader, PdfWriter - -# Create watermark (or load existing) -watermark = PdfReader("watermark.pdf").pages[0] - -# Apply to all pages -reader = PdfReader("document.pdf") -writer = PdfWriter() - -for page in reader.pages: - page.merge_page(watermark) - writer.add_page(page) - -with open("watermarked.pdf", "wb") as output: - writer.write(output) -``` - -### Extract Images -```bash -# Using pdfimages (poppler-utils) -pdfimages -j input.pdf output_prefix - -# This extracts all images as output_prefix-000.jpg, output_prefix-001.jpg, etc. -``` - -### Password Protection -```python -from pypdf import PdfReader, PdfWriter - -reader = PdfReader("input.pdf") -writer = PdfWriter() - -for page in reader.pages: - writer.add_page(page) - -# Add password -writer.encrypt("userpassword", "ownerpassword") - -with open("encrypted.pdf", "wb") as output: - writer.write(output) -``` - -## Quick Reference - -| Task | Best Tool | Command/Code | -|------|-----------|--------------| -| Merge PDFs | pypdf | `writer.add_page(page)` | -| Split PDFs | pypdf | One page per file | -| Extract text | pdfplumber | `page.extract_text()` | -| Extract tables | pdfplumber | `page.extract_tables()` | -| Create PDFs | reportlab | Canvas or Platypus | -| Command line merge | qpdf | `qpdf --empty --pages ...` | -| OCR scanned PDFs | pytesseract | Convert to image first | -| Fill PDF forms | pdf-lib or pypdf (see forms.md) | See forms.md | - -## Next Steps - -- For advanced pypdfium2 usage, see reference.md -- For JavaScript libraries (pdf-lib), see reference.md -- If you need to fill out a PDF form, follow the instructions in forms.md -- For troubleshooting guides, see reference.md diff --git a/document-skills/pdf/forms.md b/document-skills/pdf/forms.md deleted file mode 100644 index 4e234506d..000000000 --- a/document-skills/pdf/forms.md +++ /dev/null @@ -1,205 +0,0 @@ -**CRITICAL: You MUST complete these steps in order. Do not skip ahead to writing code.** - -If you need to fill out a PDF form, first check to see if the PDF has fillable form fields. Run this script from this file's directory: - `python scripts/check_fillable_fields `, and depending on the result go to either the "Fillable fields" or "Non-fillable fields" and follow those instructions. - -# Fillable fields -If the PDF has fillable form fields: -- Run this script from this file's directory: `python scripts/extract_form_field_info.py `. It will create a JSON file with a list of fields in this format: -``` -[ - { - "field_id": (unique ID for the field), - "page": (page number, 1-based), - "rect": ([left, bottom, right, top] bounding box in PDF coordinates, y=0 is the bottom of the page), - "type": ("text", "checkbox", "radio_group", or "choice"), - }, - // Checkboxes have "checked_value" and "unchecked_value" properties: - { - "field_id": (unique ID for the field), - "page": (page number, 1-based), - "type": "checkbox", - "checked_value": (Set the field to this value to check the checkbox), - "unchecked_value": (Set the field to this value to uncheck the checkbox), - }, - // Radio groups have a "radio_options" list with the possible choices. - { - "field_id": (unique ID for the field), - "page": (page number, 1-based), - "type": "radio_group", - "radio_options": [ - { - "value": (set the field to this value to select this radio option), - "rect": (bounding box for the radio button for this option) - }, - // Other radio options - ] - }, - // Multiple choice fields have a "choice_options" list with the possible choices: - { - "field_id": (unique ID for the field), - "page": (page number, 1-based), - "type": "choice", - "choice_options": [ - { - "value": (set the field to this value to select this option), - "text": (display text of the option) - }, - // Other choice options - ], - } -] -``` -- Convert the PDF to PNGs (one image for each page) with this script (run from this file's directory): -`python scripts/convert_pdf_to_images.py ` -Then analyze the images to determine the purpose of each form field (make sure to convert the bounding box PDF coordinates to image coordinates). -- Create a `field_values.json` file in this format with the values to be entered for each field: -``` -[ - { - "field_id": "last_name", // Must match the field_id from `extract_form_field_info.py` - "description": "The user's last name", - "page": 1, // Must match the "page" value in field_info.json - "value": "Simpson" - }, - { - "field_id": "Checkbox12", - "description": "Checkbox to be checked if the user is 18 or over", - "page": 1, - "value": "/On" // If this is a checkbox, use its "checked_value" value to check it. If it's a radio button group, use one of the "value" values in "radio_options". - }, - // more fields -] -``` -- Run the `fill_fillable_fields.py` script from this file's directory to create a filled-in PDF: -`python scripts/fill_fillable_fields.py ` -This script will verify that the field IDs and values you provide are valid; if it prints error messages, correct the appropriate fields and try again. - -# Non-fillable fields -If the PDF doesn't have fillable form fields, you'll need to visually determine where the data should be added and create text annotations. Follow the below steps *exactly*. You MUST perform all of these steps to ensure that the the form is accurately completed. Details for each step are below. -- Convert the PDF to PNG images and determine field bounding boxes. -- Create a JSON file with field information and validation images showing the bounding boxes. -- Validate the the bounding boxes. -- Use the bounding boxes to fill in the form. - -## Step 1: Visual Analysis (REQUIRED) -- Convert the PDF to PNG images. Run this script from this file's directory: -`python scripts/convert_pdf_to_images.py ` -The script will create a PNG image for each page in the PDF. -- Carefully examine each PNG image and identify all form fields and areas where the user should enter data. For each form field where the user should enter text, determine bounding boxes for both the form field label, and the area where the user should enter text. The label and entry bounding boxes MUST NOT INTERSECT; the text entry box should only include the area where data should be entered. Usually this area will be immediately to the side, above, or below its label. Entry bounding boxes must be tall and wide enough to contain their text. - -These are some examples of form structures that you might see: - -*Label inside box* -``` -┌────────────────────────┐ -│ Name: │ -└────────────────────────┘ -``` -The input area should be to the right of the "Name" label and extend to the edge of the box. - -*Label before line* -``` -Email: _______________________ -``` -The input area should be above the line and include its entire width. - -*Label under line* -``` -_________________________ -Name -``` -The input area should be above the line and include the entire width of the line. This is common for signature and date fields. - -*Label above line* -``` -Please enter any special requests: -________________________________________________ -``` -The input area should extend from the bottom of the label to the line, and should include the entire width of the line. - -*Checkboxes* -``` -Are you a US citizen? Yes □ No □ -``` -For checkboxes: -- Look for small square boxes (□) - these are the actual checkboxes to target. They may be to the left or right of their labels. -- Distinguish between label text ("Yes", "No") and the clickable checkbox squares. -- The entry bounding box should cover ONLY the small square, not the text label. - -### Step 2: Create fields.json and validation images (REQUIRED) -- Create a file named `fields.json` with information for the form fields and bounding boxes in this format: -``` -{ - "pages": [ - { - "page_number": 1, - "image_width": (first page image width in pixels), - "image_height": (first page image height in pixels), - }, - { - "page_number": 2, - "image_width": (second page image width in pixels), - "image_height": (second page image height in pixels), - } - // additional pages - ], - "form_fields": [ - // Example for a text field. - { - "page_number": 1, - "description": "The user's last name should be entered here", - // Bounding boxes are [left, top, right, bottom]. The bounding boxes for the label and text entry should not overlap. - "field_label": "Last name", - "label_bounding_box": [30, 125, 95, 142], - "entry_bounding_box": [100, 125, 280, 142], - "entry_text": { - "text": "Johnson", // This text will be added as an annotation at the entry_bounding_box location - "font_size": 14, // optional, defaults to 14 - "font_color": "000000", // optional, RRGGBB format, defaults to 000000 (black) - } - }, - // Example for a checkbox. TARGET THE SQUARE for the entry bounding box, NOT THE TEXT - { - "page_number": 2, - "description": "Checkbox that should be checked if the user is over 18", - "entry_bounding_box": [140, 525, 155, 540], // Small box over checkbox square - "field_label": "Yes", - "label_bounding_box": [100, 525, 132, 540], // Box containing "Yes" text - // Use "X" to check a checkbox. - "entry_text": { - "text": "X", - } - } - // additional form field entries - ] -} -``` - -Create validation images by running this script from this file's directory for each page: -`python scripts/create_validation_image.py - -The validation images will have red rectangles where text should be entered, and blue rectangles covering label text. - -### Step 3: Validate Bounding Boxes (REQUIRED) -#### Automated intersection check -- Verify that none of bounding boxes intersect and that the entry bounding boxes are tall enough by checking the fields.json file with the `check_bounding_boxes.py` script (run from this file's directory): -`python scripts/check_bounding_boxes.py ` - -If there are errors, reanalyze the relevant fields, adjust the bounding boxes, and iterate until there are no remaining errors. Remember: label (blue) bounding boxes should contain text labels, entry (red) boxes should not. - -#### Manual image inspection -**CRITICAL: Do not proceed without visually inspecting validation images** -- Red rectangles must ONLY cover input areas -- Red rectangles MUST NOT contain any text -- Blue rectangles should contain label text -- For checkboxes: - - Red rectangle MUST be centered on the checkbox square - - Blue rectangle should cover the text label for the checkbox - -- If any rectangles look wrong, fix fields.json, regenerate the validation images, and verify again. Repeat this process until the bounding boxes are fully accurate. - - -### Step 4: Add annotations to the PDF -Run this script from this file's directory to create a filled-out PDF using the information in fields.json: -`python scripts/fill_pdf_form_with_annotations.py diff --git a/document-skills/pdf/reference.md b/document-skills/pdf/reference.md deleted file mode 100644 index 41400bf4f..000000000 --- a/document-skills/pdf/reference.md +++ /dev/null @@ -1,612 +0,0 @@ -# PDF Processing Advanced Reference - -This document contains advanced PDF processing features, detailed examples, and additional libraries not covered in the main skill instructions. - -## pypdfium2 Library (Apache/BSD License) - -### Overview -pypdfium2 is a Python binding for PDFium (Chromium's PDF library). It's excellent for fast PDF rendering, image generation, and serves as a PyMuPDF replacement. - -### Render PDF to Images -```python -import pypdfium2 as pdfium -from PIL import Image - -# Load PDF -pdf = pdfium.PdfDocument("document.pdf") - -# Render page to image -page = pdf[0] # First page -bitmap = page.render( - scale=2.0, # Higher resolution - rotation=0 # No rotation -) - -# Convert to PIL Image -img = bitmap.to_pil() -img.save("page_1.png", "PNG") - -# Process multiple pages -for i, page in enumerate(pdf): - bitmap = page.render(scale=1.5) - img = bitmap.to_pil() - img.save(f"page_{i+1}.jpg", "JPEG", quality=90) -``` - -### Extract Text with pypdfium2 -```python -import pypdfium2 as pdfium - -pdf = pdfium.PdfDocument("document.pdf") -for i, page in enumerate(pdf): - text = page.get_text() - print(f"Page {i+1} text length: {len(text)} chars") -``` - -## JavaScript Libraries - -### pdf-lib (MIT License) - -pdf-lib is a powerful JavaScript library for creating and modifying PDF documents in any JavaScript environment. - -#### Load and Manipulate Existing PDF -```javascript -import { PDFDocument } from 'pdf-lib'; -import fs from 'fs'; - -async function manipulatePDF() { - // Load existing PDF - const existingPdfBytes = fs.readFileSync('input.pdf'); - const pdfDoc = await PDFDocument.load(existingPdfBytes); - - // Get page count - const pageCount = pdfDoc.getPageCount(); - console.log(`Document has ${pageCount} pages`); - - // Add new page - const newPage = pdfDoc.addPage([600, 400]); - newPage.drawText('Added by pdf-lib', { - x: 100, - y: 300, - size: 16 - }); - - // Save modified PDF - const pdfBytes = await pdfDoc.save(); - fs.writeFileSync('modified.pdf', pdfBytes); -} -``` - -#### Create Complex PDFs from Scratch -```javascript -import { PDFDocument, rgb, StandardFonts } from 'pdf-lib'; -import fs from 'fs'; - -async function createPDF() { - const pdfDoc = await PDFDocument.create(); - - // Add fonts - const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica); - const helveticaBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold); - - // Add page - const page = pdfDoc.addPage([595, 842]); // A4 size - const { width, height } = page.getSize(); - - // Add text with styling - page.drawText('Invoice #12345', { - x: 50, - y: height - 50, - size: 18, - font: helveticaBold, - color: rgb(0.2, 0.2, 0.8) - }); - - // Add rectangle (header background) - page.drawRectangle({ - x: 40, - y: height - 100, - width: width - 80, - height: 30, - color: rgb(0.9, 0.9, 0.9) - }); - - // Add table-like content - const items = [ - ['Item', 'Qty', 'Price', 'Total'], - ['Widget', '2', '$50', '$100'], - ['Gadget', '1', '$75', '$75'] - ]; - - let yPos = height - 150; - items.forEach(row => { - let xPos = 50; - row.forEach(cell => { - page.drawText(cell, { - x: xPos, - y: yPos, - size: 12, - font: helveticaFont - }); - xPos += 120; - }); - yPos -= 25; - }); - - const pdfBytes = await pdfDoc.save(); - fs.writeFileSync('created.pdf', pdfBytes); -} -``` - -#### Advanced Merge and Split Operations -```javascript -import { PDFDocument } from 'pdf-lib'; -import fs from 'fs'; - -async function mergePDFs() { - // Create new document - const mergedPdf = await PDFDocument.create(); - - // Load source PDFs - const pdf1Bytes = fs.readFileSync('doc1.pdf'); - const pdf2Bytes = fs.readFileSync('doc2.pdf'); - - const pdf1 = await PDFDocument.load(pdf1Bytes); - const pdf2 = await PDFDocument.load(pdf2Bytes); - - // Copy pages from first PDF - const pdf1Pages = await mergedPdf.copyPages(pdf1, pdf1.getPageIndices()); - pdf1Pages.forEach(page => mergedPdf.addPage(page)); - - // Copy specific pages from second PDF (pages 0, 2, 4) - const pdf2Pages = await mergedPdf.copyPages(pdf2, [0, 2, 4]); - pdf2Pages.forEach(page => mergedPdf.addPage(page)); - - const mergedPdfBytes = await mergedPdf.save(); - fs.writeFileSync('merged.pdf', mergedPdfBytes); -} -``` - -### pdfjs-dist (Apache License) - -PDF.js is Mozilla's JavaScript library for rendering PDFs in the browser. - -#### Basic PDF Loading and Rendering -```javascript -import * as pdfjsLib from 'pdfjs-dist'; - -// Configure worker (important for performance) -pdfjsLib.GlobalWorkerOptions.workerSrc = './pdf.worker.js'; - -async function renderPDF() { - // Load PDF - const loadingTask = pdfjsLib.getDocument('document.pdf'); - const pdf = await loadingTask.promise; - - console.log(`Loaded PDF with ${pdf.numPages} pages`); - - // Get first page - const page = await pdf.getPage(1); - const viewport = page.getViewport({ scale: 1.5 }); - - // Render to canvas - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - canvas.height = viewport.height; - canvas.width = viewport.width; - - const renderContext = { - canvasContext: context, - viewport: viewport - }; - - await page.render(renderContext).promise; - document.body.appendChild(canvas); -} -``` - -#### Extract Text with Coordinates -```javascript -import * as pdfjsLib from 'pdfjs-dist'; - -async function extractText() { - const loadingTask = pdfjsLib.getDocument('document.pdf'); - const pdf = await loadingTask.promise; - - let fullText = ''; - - // Extract text from all pages - for (let i = 1; i <= pdf.numPages; i++) { - const page = await pdf.getPage(i); - const textContent = await page.getTextContent(); - - const pageText = textContent.items - .map(item => item.str) - .join(' '); - - fullText += `\n--- Page ${i} ---\n${pageText}`; - - // Get text with coordinates for advanced processing - const textWithCoords = textContent.items.map(item => ({ - text: item.str, - x: item.transform[4], - y: item.transform[5], - width: item.width, - height: item.height - })); - } - - console.log(fullText); - return fullText; -} -``` - -#### Extract Annotations and Forms -```javascript -import * as pdfjsLib from 'pdfjs-dist'; - -async function extractAnnotations() { - const loadingTask = pdfjsLib.getDocument('annotated.pdf'); - const pdf = await loadingTask.promise; - - for (let i = 1; i <= pdf.numPages; i++) { - const page = await pdf.getPage(i); - const annotations = await page.getAnnotations(); - - annotations.forEach(annotation => { - console.log(`Annotation type: ${annotation.subtype}`); - console.log(`Content: ${annotation.contents}`); - console.log(`Coordinates: ${JSON.stringify(annotation.rect)}`); - }); - } -} -``` - -## Advanced Command-Line Operations - -### poppler-utils Advanced Features - -#### Extract Text with Bounding Box Coordinates -```bash -# Extract text with bounding box coordinates (essential for structured data) -pdftotext -bbox-layout document.pdf output.xml - -# The XML output contains precise coordinates for each text element -``` - -#### Advanced Image Conversion -```bash -# Convert to PNG images with specific resolution -pdftoppm -png -r 300 document.pdf output_prefix - -# Convert specific page range with high resolution -pdftoppm -png -r 600 -f 1 -l 3 document.pdf high_res_pages - -# Convert to JPEG with quality setting -pdftoppm -jpeg -jpegopt quality=85 -r 200 document.pdf jpeg_output -``` - -#### Extract Embedded Images -```bash -# Extract all embedded images with metadata -pdfimages -j -p document.pdf page_images - -# List image info without extracting -pdfimages -list document.pdf - -# Extract images in their original format -pdfimages -all document.pdf images/img -``` - -### qpdf Advanced Features - -#### Complex Page Manipulation -```bash -# Split PDF into groups of pages -qpdf --split-pages=3 input.pdf output_group_%02d.pdf - -# Extract specific pages with complex ranges -qpdf input.pdf --pages input.pdf 1,3-5,8,10-end -- extracted.pdf - -# Merge specific pages from multiple PDFs -qpdf --empty --pages doc1.pdf 1-3 doc2.pdf 5-7 doc3.pdf 2,4 -- combined.pdf -``` - -#### PDF Optimization and Repair -```bash -# Optimize PDF for web (linearize for streaming) -qpdf --linearize input.pdf optimized.pdf - -# Remove unused objects and compress -qpdf --optimize-level=all input.pdf compressed.pdf - -# Attempt to repair corrupted PDF structure -qpdf --check input.pdf -qpdf --fix-qdf damaged.pdf repaired.pdf - -# Show detailed PDF structure for debugging -qpdf --show-all-pages input.pdf > structure.txt -``` - -#### Advanced Encryption -```bash -# Add password protection with specific permissions -qpdf --encrypt user_pass owner_pass 256 --print=none --modify=none -- input.pdf encrypted.pdf - -# Check encryption status -qpdf --show-encryption encrypted.pdf - -# Remove password protection (requires password) -qpdf --password=secret123 --decrypt encrypted.pdf decrypted.pdf -``` - -## Advanced Python Techniques - -### pdfplumber Advanced Features - -#### Extract Text with Precise Coordinates -```python -import pdfplumber - -with pdfplumber.open("document.pdf") as pdf: - page = pdf.pages[0] - - # Extract all text with coordinates - chars = page.chars - for char in chars[:10]: # First 10 characters - print(f"Char: '{char['text']}' at x:{char['x0']:.1f} y:{char['y0']:.1f}") - - # Extract text by bounding box (left, top, right, bottom) - bbox_text = page.within_bbox((100, 100, 400, 200)).extract_text() -``` - -#### Advanced Table Extraction with Custom Settings -```python -import pdfplumber -import pandas as pd - -with pdfplumber.open("complex_table.pdf") as pdf: - page = pdf.pages[0] - - # Extract tables with custom settings for complex layouts - table_settings = { - "vertical_strategy": "lines", - "horizontal_strategy": "lines", - "snap_tolerance": 3, - "intersection_tolerance": 15 - } - tables = page.extract_tables(table_settings) - - # Visual debugging for table extraction - img = page.to_image(resolution=150) - img.save("debug_layout.png") -``` - -### reportlab Advanced Features - -#### Create Professional Reports with Tables -```python -from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.lib import colors - -# Sample data -data = [ - ['Product', 'Q1', 'Q2', 'Q3', 'Q4'], - ['Widgets', '120', '135', '142', '158'], - ['Gadgets', '85', '92', '98', '105'] -] - -# Create PDF with table -doc = SimpleDocTemplate("report.pdf") -elements = [] - -# Add title -styles = getSampleStyleSheet() -title = Paragraph("Quarterly Sales Report", styles['Title']) -elements.append(title) - -# Add table with advanced styling -table = Table(data) -table.setStyle(TableStyle([ - ('BACKGROUND', (0, 0), (-1, 0), colors.grey), - ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), - ('FONTSIZE', (0, 0), (-1, 0), 14), - ('BOTTOMPADDING', (0, 0), (-1, 0), 12), - ('BACKGROUND', (0, 1), (-1, -1), colors.beige), - ('GRID', (0, 0), (-1, -1), 1, colors.black) -])) -elements.append(table) - -doc.build(elements) -``` - -## Complex Workflows - -### Extract Figures/Images from PDF - -#### Method 1: Using pdfimages (fastest) -```bash -# Extract all images with original quality -pdfimages -all document.pdf images/img -``` - -#### Method 2: Using pypdfium2 + Image Processing -```python -import pypdfium2 as pdfium -from PIL import Image -import numpy as np - -def extract_figures(pdf_path, output_dir): - pdf = pdfium.PdfDocument(pdf_path) - - for page_num, page in enumerate(pdf): - # Render high-resolution page - bitmap = page.render(scale=3.0) - img = bitmap.to_pil() - - # Convert to numpy for processing - img_array = np.array(img) - - # Simple figure detection (non-white regions) - mask = np.any(img_array != [255, 255, 255], axis=2) - - # Find contours and extract bounding boxes - # (This is simplified - real implementation would need more sophisticated detection) - - # Save detected figures - # ... implementation depends on specific needs -``` - -### Batch PDF Processing with Error Handling -```python -import os -import glob -from pypdf import PdfReader, PdfWriter -import logging - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -def batch_process_pdfs(input_dir, operation='merge'): - pdf_files = glob.glob(os.path.join(input_dir, "*.pdf")) - - if operation == 'merge': - writer = PdfWriter() - for pdf_file in pdf_files: - try: - reader = PdfReader(pdf_file) - for page in reader.pages: - writer.add_page(page) - logger.info(f"Processed: {pdf_file}") - except Exception as e: - logger.error(f"Failed to process {pdf_file}: {e}") - continue - - with open("batch_merged.pdf", "wb") as output: - writer.write(output) - - elif operation == 'extract_text': - for pdf_file in pdf_files: - try: - reader = PdfReader(pdf_file) - text = "" - for page in reader.pages: - text += page.extract_text() - - output_file = pdf_file.replace('.pdf', '.txt') - with open(output_file, 'w', encoding='utf-8') as f: - f.write(text) - logger.info(f"Extracted text from: {pdf_file}") - - except Exception as e: - logger.error(f"Failed to extract text from {pdf_file}: {e}") - continue -``` - -### Advanced PDF Cropping -```python -from pypdf import PdfWriter, PdfReader - -reader = PdfReader("input.pdf") -writer = PdfWriter() - -# Crop page (left, bottom, right, top in points) -page = reader.pages[0] -page.mediabox.left = 50 -page.mediabox.bottom = 50 -page.mediabox.right = 550 -page.mediabox.top = 750 - -writer.add_page(page) -with open("cropped.pdf", "wb") as output: - writer.write(output) -``` - -## Performance Optimization Tips - -### 1. For Large PDFs -- Use streaming approaches instead of loading entire PDF in memory -- Use `qpdf --split-pages` for splitting large files -- Process pages individually with pypdfium2 - -### 2. For Text Extraction -- `pdftotext -bbox-layout` is fastest for plain text extraction -- Use pdfplumber for structured data and tables -- Avoid `pypdf.extract_text()` for very large documents - -### 3. For Image Extraction -- `pdfimages` is much faster than rendering pages -- Use low resolution for previews, high resolution for final output - -### 4. For Form Filling -- pdf-lib maintains form structure better than most alternatives -- Pre-validate form fields before processing - -### 5. Memory Management -```python -# Process PDFs in chunks -def process_large_pdf(pdf_path, chunk_size=10): - reader = PdfReader(pdf_path) - total_pages = len(reader.pages) - - for start_idx in range(0, total_pages, chunk_size): - end_idx = min(start_idx + chunk_size, total_pages) - writer = PdfWriter() - - for i in range(start_idx, end_idx): - writer.add_page(reader.pages[i]) - - # Process chunk - with open(f"chunk_{start_idx//chunk_size}.pdf", "wb") as output: - writer.write(output) -``` - -## Troubleshooting Common Issues - -### Encrypted PDFs -```python -# Handle password-protected PDFs -from pypdf import PdfReader - -try: - reader = PdfReader("encrypted.pdf") - if reader.is_encrypted: - reader.decrypt("password") -except Exception as e: - print(f"Failed to decrypt: {e}") -``` - -### Corrupted PDFs -```bash -# Use qpdf to repair -qpdf --check corrupted.pdf -qpdf --replace-input corrupted.pdf -``` - -### Text Extraction Issues -```python -# Fallback to OCR for scanned PDFs -import pytesseract -from pdf2image import convert_from_path - -def extract_text_with_ocr(pdf_path): - images = convert_from_path(pdf_path) - text = "" - for i, image in enumerate(images): - text += pytesseract.image_to_string(image) - return text -``` - -## License Information - -- **pypdf**: BSD License -- **pdfplumber**: MIT License -- **pypdfium2**: Apache/BSD License -- **reportlab**: BSD License -- **poppler-utils**: GPL-2 License -- **qpdf**: Apache License -- **pdf-lib**: MIT License -- **pdfjs-dist**: Apache License \ No newline at end of file diff --git a/document-skills/pdf/scripts/check_bounding_boxes.py b/document-skills/pdf/scripts/check_bounding_boxes.py deleted file mode 100644 index 7443660c0..000000000 --- a/document-skills/pdf/scripts/check_bounding_boxes.py +++ /dev/null @@ -1,70 +0,0 @@ -from dataclasses import dataclass -import json -import sys - - -# Script to check that the `fields.json` file that Claude creates when analyzing PDFs -# does not have overlapping bounding boxes. See forms.md. - - -@dataclass -class RectAndField: - rect: list[float] - rect_type: str - field: dict - - -# Returns a list of messages that are printed to stdout for Claude to read. -def get_bounding_box_messages(fields_json_stream) -> list[str]: - messages = [] - fields = json.load(fields_json_stream) - messages.append(f"Read {len(fields['form_fields'])} fields") - - def rects_intersect(r1, r2): - disjoint_horizontal = r1[0] >= r2[2] or r1[2] <= r2[0] - disjoint_vertical = r1[1] >= r2[3] or r1[3] <= r2[1] - return not (disjoint_horizontal or disjoint_vertical) - - rects_and_fields = [] - for f in fields["form_fields"]: - rects_and_fields.append(RectAndField(f["label_bounding_box"], "label", f)) - rects_and_fields.append(RectAndField(f["entry_bounding_box"], "entry", f)) - - has_error = False - for i, ri in enumerate(rects_and_fields): - # This is O(N^2); we can optimize if it becomes a problem. - for j in range(i + 1, len(rects_and_fields)): - rj = rects_and_fields[j] - if ri.field["page_number"] == rj.field["page_number"] and rects_intersect(ri.rect, rj.rect): - has_error = True - if ri.field is rj.field: - messages.append(f"FAILURE: intersection between label and entry bounding boxes for `{ri.field['description']}` ({ri.rect}, {rj.rect})") - else: - messages.append(f"FAILURE: intersection between {ri.rect_type} bounding box for `{ri.field['description']}` ({ri.rect}) and {rj.rect_type} bounding box for `{rj.field['description']}` ({rj.rect})") - if len(messages) >= 20: - messages.append("Aborting further checks; fix bounding boxes and try again") - return messages - if ri.rect_type == "entry": - if "entry_text" in ri.field: - font_size = ri.field["entry_text"].get("font_size", 14) - entry_height = ri.rect[3] - ri.rect[1] - if entry_height < font_size: - has_error = True - messages.append(f"FAILURE: entry bounding box height ({entry_height}) for `{ri.field['description']}` is too short for the text content (font size: {font_size}). Increase the box height or decrease the font size.") - if len(messages) >= 20: - messages.append("Aborting further checks; fix bounding boxes and try again") - return messages - - if not has_error: - messages.append("SUCCESS: All bounding boxes are valid") - return messages - -if __name__ == "__main__": - if len(sys.argv) != 2: - print("Usage: check_bounding_boxes.py [fields.json]") - sys.exit(1) - # Input file should be in the `fields.json` format described in forms.md. - with open(sys.argv[1]) as f: - messages = get_bounding_box_messages(f) - for msg in messages: - print(msg) diff --git a/document-skills/pdf/scripts/check_bounding_boxes_test.py b/document-skills/pdf/scripts/check_bounding_boxes_test.py deleted file mode 100644 index 1dbb463c8..000000000 --- a/document-skills/pdf/scripts/check_bounding_boxes_test.py +++ /dev/null @@ -1,226 +0,0 @@ -import unittest -import json -import io -from check_bounding_boxes import get_bounding_box_messages - - -# Currently this is not run automatically in CI; it's just for documentation and manual checking. -class TestGetBoundingBoxMessages(unittest.TestCase): - - def create_json_stream(self, data): - """Helper to create a JSON stream from data""" - return io.StringIO(json.dumps(data)) - - def test_no_intersections(self): - """Test case with no bounding box intersections""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 30] - }, - { - "description": "Email", - "page_number": 1, - "label_bounding_box": [10, 40, 50, 60], - "entry_bounding_box": [60, 40, 150, 60] - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("SUCCESS" in msg for msg in messages)) - self.assertFalse(any("FAILURE" in msg for msg in messages)) - - def test_label_entry_intersection_same_field(self): - """Test intersection between label and entry of the same field""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 60, 30], - "entry_bounding_box": [50, 10, 150, 30] # Overlaps with label - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("FAILURE" in msg and "intersection" in msg for msg in messages)) - self.assertFalse(any("SUCCESS" in msg for msg in messages)) - - def test_intersection_between_different_fields(self): - """Test intersection between bounding boxes of different fields""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 30] - }, - { - "description": "Email", - "page_number": 1, - "label_bounding_box": [40, 20, 80, 40], # Overlaps with Name's boxes - "entry_bounding_box": [160, 10, 250, 30] - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("FAILURE" in msg and "intersection" in msg for msg in messages)) - self.assertFalse(any("SUCCESS" in msg for msg in messages)) - - def test_different_pages_no_intersection(self): - """Test that boxes on different pages don't count as intersecting""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 30] - }, - { - "description": "Email", - "page_number": 2, - "label_bounding_box": [10, 10, 50, 30], # Same coordinates but different page - "entry_bounding_box": [60, 10, 150, 30] - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("SUCCESS" in msg for msg in messages)) - self.assertFalse(any("FAILURE" in msg for msg in messages)) - - def test_entry_height_too_small(self): - """Test that entry box height is checked against font size""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 20], # Height is 10 - "entry_text": { - "font_size": 14 # Font size larger than height - } - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("FAILURE" in msg and "height" in msg for msg in messages)) - self.assertFalse(any("SUCCESS" in msg for msg in messages)) - - def test_entry_height_adequate(self): - """Test that adequate entry box height passes""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 30], # Height is 20 - "entry_text": { - "font_size": 14 # Font size smaller than height - } - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("SUCCESS" in msg for msg in messages)) - self.assertFalse(any("FAILURE" in msg for msg in messages)) - - def test_default_font_size(self): - """Test that default font size is used when not specified""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 20], # Height is 10 - "entry_text": {} # No font_size specified, should use default 14 - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("FAILURE" in msg and "height" in msg for msg in messages)) - self.assertFalse(any("SUCCESS" in msg for msg in messages)) - - def test_no_entry_text(self): - """Test that missing entry_text doesn't cause height check""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [60, 10, 150, 20] # Small height but no entry_text - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("SUCCESS" in msg for msg in messages)) - self.assertFalse(any("FAILURE" in msg for msg in messages)) - - def test_multiple_errors_limit(self): - """Test that error messages are limited to prevent excessive output""" - fields = [] - # Create many overlapping fields - for i in range(25): - fields.append({ - "description": f"Field{i}", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], # All overlap - "entry_bounding_box": [20, 15, 60, 35] # All overlap - }) - - data = {"form_fields": fields} - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - # Should abort after ~20 messages - self.assertTrue(any("Aborting" in msg for msg in messages)) - # Should have some FAILURE messages but not hundreds - failure_count = sum(1 for msg in messages if "FAILURE" in msg) - self.assertGreater(failure_count, 0) - self.assertLess(len(messages), 30) # Should be limited - - def test_edge_touching_boxes(self): - """Test that boxes touching at edges don't count as intersecting""" - data = { - "form_fields": [ - { - "description": "Name", - "page_number": 1, - "label_bounding_box": [10, 10, 50, 30], - "entry_bounding_box": [50, 10, 150, 30] # Touches at x=50 - } - ] - } - - stream = self.create_json_stream(data) - messages = get_bounding_box_messages(stream) - self.assertTrue(any("SUCCESS" in msg for msg in messages)) - self.assertFalse(any("FAILURE" in msg for msg in messages)) - - -if __name__ == '__main__': - unittest.main() diff --git a/document-skills/pdf/scripts/check_fillable_fields.py b/document-skills/pdf/scripts/check_fillable_fields.py deleted file mode 100644 index dc43d1821..000000000 --- a/document-skills/pdf/scripts/check_fillable_fields.py +++ /dev/null @@ -1,12 +0,0 @@ -import sys -from pypdf import PdfReader - - -# Script for Claude to run to determine whether a PDF has fillable form fields. See forms.md. - - -reader = PdfReader(sys.argv[1]) -if (reader.get_fields()): - print("This PDF has fillable form fields") -else: - print("This PDF does not have fillable form fields; you will need to visually determine where to enter data") diff --git a/document-skills/pdf/scripts/convert_pdf_to_images.py b/document-skills/pdf/scripts/convert_pdf_to_images.py deleted file mode 100644 index f8a4ec524..000000000 --- a/document-skills/pdf/scripts/convert_pdf_to_images.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import sys - -from pdf2image import convert_from_path - - -# Converts each page of a PDF to a PNG image. - - -def convert(pdf_path, output_dir, max_dim=1000): - images = convert_from_path(pdf_path, dpi=200) - - for i, image in enumerate(images): - # Scale image if needed to keep width/height under `max_dim` - width, height = image.size - if width > max_dim or height > max_dim: - scale_factor = min(max_dim / width, max_dim / height) - new_width = int(width * scale_factor) - new_height = int(height * scale_factor) - image = image.resize((new_width, new_height)) - - image_path = os.path.join(output_dir, f"page_{i+1}.png") - image.save(image_path) - print(f"Saved page {i+1} as {image_path} (size: {image.size})") - - print(f"Converted {len(images)} pages to PNG images") - - -if __name__ == "__main__": - if len(sys.argv) != 3: - print("Usage: convert_pdf_to_images.py [input pdf] [output directory]") - sys.exit(1) - pdf_path = sys.argv[1] - output_directory = sys.argv[2] - convert(pdf_path, output_directory) diff --git a/document-skills/pdf/scripts/create_validation_image.py b/document-skills/pdf/scripts/create_validation_image.py deleted file mode 100644 index 4913f8f8d..000000000 --- a/document-skills/pdf/scripts/create_validation_image.py +++ /dev/null @@ -1,41 +0,0 @@ -import json -import sys - -from PIL import Image, ImageDraw - - -# Creates "validation" images with rectangles for the bounding box information that -# Claude creates when determining where to add text annotations in PDFs. See forms.md. - - -def create_validation_image(page_number, fields_json_path, input_path, output_path): - # Input file should be in the `fields.json` format described in forms.md. - with open(fields_json_path, 'r') as f: - data = json.load(f) - - img = Image.open(input_path) - draw = ImageDraw.Draw(img) - num_boxes = 0 - - for field in data["form_fields"]: - if field["page_number"] == page_number: - entry_box = field['entry_bounding_box'] - label_box = field['label_bounding_box'] - # Draw red rectangle over entry bounding box and blue rectangle over the label. - draw.rectangle(entry_box, outline='red', width=2) - draw.rectangle(label_box, outline='blue', width=2) - num_boxes += 2 - - img.save(output_path) - print(f"Created validation image at {output_path} with {num_boxes} bounding boxes") - - -if __name__ == "__main__": - if len(sys.argv) != 5: - print("Usage: create_validation_image.py [page number] [fields.json file] [input image path] [output image path]") - sys.exit(1) - page_number = int(sys.argv[1]) - fields_json_path = sys.argv[2] - input_image_path = sys.argv[3] - output_image_path = sys.argv[4] - create_validation_image(page_number, fields_json_path, input_image_path, output_image_path) diff --git a/document-skills/pdf/scripts/extract_form_field_info.py b/document-skills/pdf/scripts/extract_form_field_info.py deleted file mode 100644 index f42a2df84..000000000 --- a/document-skills/pdf/scripts/extract_form_field_info.py +++ /dev/null @@ -1,152 +0,0 @@ -import json -import sys - -from pypdf import PdfReader - - -# Extracts data for the fillable form fields in a PDF and outputs JSON that -# Claude uses to fill the fields. See forms.md. - - -# This matches the format used by PdfReader `get_fields` and `update_page_form_field_values` methods. -def get_full_annotation_field_id(annotation): - components = [] - while annotation: - field_name = annotation.get('/T') - if field_name: - components.append(field_name) - annotation = annotation.get('/Parent') - return ".".join(reversed(components)) if components else None - - -def make_field_dict(field, field_id): - field_dict = {"field_id": field_id} - ft = field.get('/FT') - if ft == "/Tx": - field_dict["type"] = "text" - elif ft == "/Btn": - field_dict["type"] = "checkbox" # radio groups handled separately - states = field.get("/_States_", []) - if len(states) == 2: - # "/Off" seems to always be the unchecked value, as suggested by - # https://opensource.adobe.com/dc-acrobat-sdk-docs/standards/pdfstandards/pdf/PDF32000_2008.pdf#page=448 - # It can be either first or second in the "/_States_" list. - if "/Off" in states: - field_dict["checked_value"] = states[0] if states[0] != "/Off" else states[1] - field_dict["unchecked_value"] = "/Off" - else: - print(f"Unexpected state values for checkbox `${field_id}`. Its checked and unchecked values may not be correct; if you're trying to check it, visually verify the results.") - field_dict["checked_value"] = states[0] - field_dict["unchecked_value"] = states[1] - elif ft == "/Ch": - field_dict["type"] = "choice" - states = field.get("/_States_", []) - field_dict["choice_options"] = [{ - "value": state[0], - "text": state[1], - } for state in states] - else: - field_dict["type"] = f"unknown ({ft})" - return field_dict - - -# Returns a list of fillable PDF fields: -# [ -# { -# "field_id": "name", -# "page": 1, -# "type": ("text", "checkbox", "radio_group", or "choice") -# // Per-type additional fields described in forms.md -# }, -# ] -def get_field_info(reader: PdfReader): - fields = reader.get_fields() - - field_info_by_id = {} - possible_radio_names = set() - - for field_id, field in fields.items(): - # Skip if this is a container field with children, except that it might be - # a parent group for radio button options. - if field.get("/Kids"): - if field.get("/FT") == "/Btn": - possible_radio_names.add(field_id) - continue - field_info_by_id[field_id] = make_field_dict(field, field_id) - - # Bounding rects are stored in annotations in page objects. - - # Radio button options have a separate annotation for each choice; - # all choices have the same field name. - # See https://westhealth.github.io/exploring-fillable-forms-with-pdfrw.html - radio_fields_by_id = {} - - for page_index, page in enumerate(reader.pages): - annotations = page.get('/Annots', []) - for ann in annotations: - field_id = get_full_annotation_field_id(ann) - if field_id in field_info_by_id: - field_info_by_id[field_id]["page"] = page_index + 1 - field_info_by_id[field_id]["rect"] = ann.get('/Rect') - elif field_id in possible_radio_names: - try: - # ann['/AP']['/N'] should have two items. One of them is '/Off', - # the other is the active value. - on_values = [v for v in ann["/AP"]["/N"] if v != "/Off"] - except KeyError: - continue - if len(on_values) == 1: - rect = ann.get("/Rect") - if field_id not in radio_fields_by_id: - radio_fields_by_id[field_id] = { - "field_id": field_id, - "type": "radio_group", - "page": page_index + 1, - "radio_options": [], - } - # Note: at least on macOS 15.7, Preview.app doesn't show selected - # radio buttons correctly. (It does if you remove the leading slash - # from the value, but that causes them not to appear correctly in - # Chrome/Firefox/Acrobat/etc). - radio_fields_by_id[field_id]["radio_options"].append({ - "value": on_values[0], - "rect": rect, - }) - - # Some PDFs have form field definitions without corresponding annotations, - # so we can't tell where they are. Ignore these fields for now. - fields_with_location = [] - for field_info in field_info_by_id.values(): - if "page" in field_info: - fields_with_location.append(field_info) - else: - print(f"Unable to determine location for field id: {field_info.get('field_id')}, ignoring") - - # Sort by page number, then Y position (flipped in PDF coordinate system), then X. - def sort_key(f): - if "radio_options" in f: - rect = f["radio_options"][0]["rect"] or [0, 0, 0, 0] - else: - rect = f.get("rect") or [0, 0, 0, 0] - adjusted_position = [-rect[1], rect[0]] - return [f.get("page"), adjusted_position] - - sorted_fields = fields_with_location + list(radio_fields_by_id.values()) - sorted_fields.sort(key=sort_key) - - return sorted_fields - - -def write_field_info(pdf_path: str, json_output_path: str): - reader = PdfReader(pdf_path) - field_info = get_field_info(reader) - with open(json_output_path, "w") as f: - json.dump(field_info, f, indent=2) - print(f"Wrote {len(field_info)} fields to {json_output_path}") - - -if __name__ == "__main__": - if len(sys.argv) != 3: - print("Usage: extract_form_field_info.py [input pdf] [output json]") - sys.exit(1) - write_field_info(sys.argv[1], sys.argv[2]) diff --git a/document-skills/pdf/scripts/fill_fillable_fields.py b/document-skills/pdf/scripts/fill_fillable_fields.py deleted file mode 100644 index ac35753c5..000000000 --- a/document-skills/pdf/scripts/fill_fillable_fields.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -import sys - -from pypdf import PdfReader, PdfWriter - -from extract_form_field_info import get_field_info - - -# Fills fillable form fields in a PDF. See forms.md. - - -def fill_pdf_fields(input_pdf_path: str, fields_json_path: str, output_pdf_path: str): - with open(fields_json_path) as f: - fields = json.load(f) - # Group by page number. - fields_by_page = {} - for field in fields: - if "value" in field: - field_id = field["field_id"] - page = field["page"] - if page not in fields_by_page: - fields_by_page[page] = {} - fields_by_page[page][field_id] = field["value"] - - reader = PdfReader(input_pdf_path) - - has_error = False - field_info = get_field_info(reader) - fields_by_ids = {f["field_id"]: f for f in field_info} - for field in fields: - existing_field = fields_by_ids.get(field["field_id"]) - if not existing_field: - has_error = True - print(f"ERROR: `{field['field_id']}` is not a valid field ID") - elif field["page"] != existing_field["page"]: - has_error = True - print(f"ERROR: Incorrect page number for `{field['field_id']}` (got {field['page']}, expected {existing_field['page']})") - else: - if "value" in field: - err = validation_error_for_field_value(existing_field, field["value"]) - if err: - print(err) - has_error = True - if has_error: - sys.exit(1) - - writer = PdfWriter(clone_from=reader) - for page, field_values in fields_by_page.items(): - writer.update_page_form_field_values(writer.pages[page - 1], field_values, auto_regenerate=False) - - # This seems to be necessary for many PDF viewers to format the form values correctly. - # It may cause the viewer to show a "save changes" dialog even if the user doesn't make any changes. - writer.set_need_appearances_writer(True) - - with open(output_pdf_path, "wb") as f: - writer.write(f) - - -def validation_error_for_field_value(field_info, field_value): - field_type = field_info["type"] - field_id = field_info["field_id"] - if field_type == "checkbox": - checked_val = field_info["checked_value"] - unchecked_val = field_info["unchecked_value"] - if field_value != checked_val and field_value != unchecked_val: - return f'ERROR: Invalid value "{field_value}" for checkbox field "{field_id}". The checked value is "{checked_val}" and the unchecked value is "{unchecked_val}"' - elif field_type == "radio_group": - option_values = [opt["value"] for opt in field_info["radio_options"]] - if field_value not in option_values: - return f'ERROR: Invalid value "{field_value}" for radio group field "{field_id}". Valid values are: {option_values}' - elif field_type == "choice": - choice_values = [opt["value"] for opt in field_info["choice_options"]] - if field_value not in choice_values: - return f'ERROR: Invalid value "{field_value}" for choice field "{field_id}". Valid values are: {choice_values}' - return None - - -# pypdf (at least version 5.7.0) has a bug when setting the value for a selection list field. -# In _writer.py around line 966: -# -# if field.get(FA.FT, "/Tx") == "/Ch" and field_flags & FA.FfBits.Combo == 0: -# txt = "\n".join(annotation.get_inherited(FA.Opt, [])) -# -# The problem is that for selection lists, `get_inherited` returns a list of two-element lists like -# [["value1", "Text 1"], ["value2", "Text 2"], ...] -# This causes `join` to throw a TypeError because it expects an iterable of strings. -# The horrible workaround is to patch `get_inherited` to return a list of the value strings. -# We call the original method and adjust the return value only if the argument to `get_inherited` -# is `FA.Opt` and if the return value is a list of two-element lists. -def monkeypatch_pydpf_method(): - from pypdf.generic import DictionaryObject - from pypdf.constants import FieldDictionaryAttributes - - original_get_inherited = DictionaryObject.get_inherited - - def patched_get_inherited(self, key: str, default = None): - result = original_get_inherited(self, key, default) - if key == FieldDictionaryAttributes.Opt: - if isinstance(result, list) and all(isinstance(v, list) and len(v) == 2 for v in result): - result = [r[0] for r in result] - return result - - DictionaryObject.get_inherited = patched_get_inherited - - -if __name__ == "__main__": - if len(sys.argv) != 4: - print("Usage: fill_fillable_fields.py [input pdf] [field_values.json] [output pdf]") - sys.exit(1) - monkeypatch_pydpf_method() - input_pdf = sys.argv[1] - fields_json = sys.argv[2] - output_pdf = sys.argv[3] - fill_pdf_fields(input_pdf, fields_json, output_pdf) diff --git a/document-skills/pdf/scripts/fill_pdf_form_with_annotations.py b/document-skills/pdf/scripts/fill_pdf_form_with_annotations.py deleted file mode 100644 index f98053135..000000000 --- a/document-skills/pdf/scripts/fill_pdf_form_with_annotations.py +++ /dev/null @@ -1,108 +0,0 @@ -import json -import sys - -from pypdf import PdfReader, PdfWriter -from pypdf.annotations import FreeText - - -# Fills a PDF by adding text annotations defined in `fields.json`. See forms.md. - - -def transform_coordinates(bbox, image_width, image_height, pdf_width, pdf_height): - """Transform bounding box from image coordinates to PDF coordinates""" - # Image coordinates: origin at top-left, y increases downward - # PDF coordinates: origin at bottom-left, y increases upward - x_scale = pdf_width / image_width - y_scale = pdf_height / image_height - - left = bbox[0] * x_scale - right = bbox[2] * x_scale - - # Flip Y coordinates for PDF - top = pdf_height - (bbox[1] * y_scale) - bottom = pdf_height - (bbox[3] * y_scale) - - return left, bottom, right, top - - -def fill_pdf_form(input_pdf_path, fields_json_path, output_pdf_path): - """Fill the PDF form with data from fields.json""" - - # `fields.json` format described in forms.md. - with open(fields_json_path, "r") as f: - fields_data = json.load(f) - - # Open the PDF - reader = PdfReader(input_pdf_path) - writer = PdfWriter() - - # Copy all pages to writer - writer.append(reader) - - # Get PDF dimensions for each page - pdf_dimensions = {} - for i, page in enumerate(reader.pages): - mediabox = page.mediabox - pdf_dimensions[i + 1] = [mediabox.width, mediabox.height] - - # Process each form field - annotations = [] - for field in fields_data["form_fields"]: - page_num = field["page_number"] - - # Get page dimensions and transform coordinates. - page_info = next(p for p in fields_data["pages"] if p["page_number"] == page_num) - image_width = page_info["image_width"] - image_height = page_info["image_height"] - pdf_width, pdf_height = pdf_dimensions[page_num] - - transformed_entry_box = transform_coordinates( - field["entry_bounding_box"], - image_width, image_height, - pdf_width, pdf_height - ) - - # Skip empty fields - if "entry_text" not in field or "text" not in field["entry_text"]: - continue - entry_text = field["entry_text"] - text = entry_text["text"] - if not text: - continue - - font_name = entry_text.get("font", "Arial") - font_size = str(entry_text.get("font_size", 14)) + "pt" - font_color = entry_text.get("font_color", "000000") - - # Font size/color seems to not work reliably across viewers: - # https://github.com/py-pdf/pypdf/issues/2084 - annotation = FreeText( - text=text, - rect=transformed_entry_box, - font=font_name, - font_size=font_size, - font_color=font_color, - border_color=None, - background_color=None, - ) - annotations.append(annotation) - # page_number is 0-based for pypdf - writer.add_annotation(page_number=page_num - 1, annotation=annotation) - - # Save the filled PDF - with open(output_pdf_path, "wb") as output: - writer.write(output) - - print(f"Successfully filled PDF form and saved to {output_pdf_path}") - print(f"Added {len(annotations)} text annotations") - - -if __name__ == "__main__": - if len(sys.argv) != 4: - print("Usage: fill_pdf_form_with_annotations.py [input pdf] [fields.json] [output pdf]") - sys.exit(1) - input_pdf = sys.argv[1] - fields_json = sys.argv[2] - output_pdf = sys.argv[3] - - fill_pdf_form(input_pdf, fields_json, output_pdf) \ No newline at end of file diff --git a/document-skills/pptx/LICENSE.txt b/document-skills/pptx/LICENSE.txt deleted file mode 100644 index c55ab4222..000000000 --- a/document-skills/pptx/LICENSE.txt +++ /dev/null @@ -1,30 +0,0 @@ -© 2025 Anthropic, PBC. All rights reserved. - -LICENSE: Use of these materials (including all code, prompts, assets, files, -and other components of this Skill) is governed by your agreement with -Anthropic regarding use of Anthropic's services. If no separate agreement -exists, use is governed by Anthropic's Consumer Terms of Service or -Commercial Terms of Service, as applicable: -https://www.anthropic.com/legal/consumer-terms -https://www.anthropic.com/legal/commercial-terms -Your applicable agreement is referred to as the "Agreement." "Services" are -as defined in the Agreement. - -ADDITIONAL RESTRICTIONS: Notwithstanding anything in the Agreement to the -contrary, users may not: - -- Extract these materials from the Services or retain copies of these - materials outside the Services -- Reproduce or copy these materials, except for temporary copies created - automatically during authorized use of the Services -- Create derivative works based on these materials -- Distribute, sublicense, or transfer these materials to any third party -- Make, offer to sell, sell, or import any inventions embodied in these - materials -- Reverse engineer, decompile, or disassemble these materials - -The receipt, viewing, or possession of these materials does not convey or -imply any license or right beyond those expressly granted above. - -Anthropic retains all right, title, and interest in these materials, -including all copyrights, patents, and other intellectual property rights. diff --git a/document-skills/pptx/SKILL.md b/document-skills/pptx/SKILL.md deleted file mode 100644 index b93b875fe..000000000 --- a/document-skills/pptx/SKILL.md +++ /dev/null @@ -1,484 +0,0 @@ ---- -name: pptx -description: "Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks" -license: Proprietary. LICENSE.txt has complete terms ---- - -# PPTX creation, editing, and analysis - -## Overview - -A user may ask you to create, edit, or analyze the contents of a .pptx file. A .pptx file is essentially a ZIP archive containing XML files and other resources that you can read or edit. You have different tools and workflows available for different tasks. - -## Reading and analyzing content - -### Text extraction -If you just need to read the text contents of a presentation, you should convert the document to markdown: - -```bash -# Convert document to markdown -python -m markitdown path-to-file.pptx -``` - -### Raw XML access -You need raw XML access for: comments, speaker notes, slide layouts, animations, design elements, and complex formatting. For any of these features, you'll need to unpack a presentation and read its raw XML contents. - -#### Unpacking a file -`python ooxml/scripts/unpack.py ` - -**Note**: The unpack.py script is located at `skills/pptx/ooxml/scripts/unpack.py` relative to the project root. If the script doesn't exist at this path, use `find . -name "unpack.py"` to locate it. - -#### Key file structures -* `ppt/presentation.xml` - Main presentation metadata and slide references -* `ppt/slides/slide{N}.xml` - Individual slide contents (slide1.xml, slide2.xml, etc.) -* `ppt/notesSlides/notesSlide{N}.xml` - Speaker notes for each slide -* `ppt/comments/modernComment_*.xml` - Comments for specific slides -* `ppt/slideLayouts/` - Layout templates for slides -* `ppt/slideMasters/` - Master slide templates -* `ppt/theme/` - Theme and styling information -* `ppt/media/` - Images and other media files - -#### Typography and color extraction -**When given an example design to emulate**: Always analyze the presentation's typography and colors first using the methods below: -1. **Read theme file**: Check `ppt/theme/theme1.xml` for colors (``) and fonts (``) -2. **Sample slide content**: Examine `ppt/slides/slide1.xml` for actual font usage (``) and colors -3. **Search for patterns**: Use grep to find color (``, ``) and font references across all XML files - -## Creating a new PowerPoint presentation **without a template** - -When creating a new PowerPoint presentation from scratch, use the **html2pptx** workflow to convert HTML slides to PowerPoint with accurate positioning. - -### Design Principles - -**CRITICAL**: Before creating any presentation, analyze the content and choose appropriate design elements: -1. **Consider the subject matter**: What is this presentation about? What tone, industry, or mood does it suggest? -2. **Check for branding**: If the user mentions a company/organization, consider their brand colors and identity -3. **Match palette to content**: Select colors that reflect the subject -4. **State your approach**: Explain your design choices before writing code - -**Requirements**: -- ✅ State your content-informed design approach BEFORE writing code -- ✅ Use web-safe fonts only: Arial, Helvetica, Times New Roman, Georgia, Courier New, Verdana, Tahoma, Trebuchet MS, Impact -- ✅ Create clear visual hierarchy through size, weight, and color -- ✅ Ensure readability: strong contrast, appropriately sized text, clean alignment -- ✅ Be consistent: repeat patterns, spacing, and visual language across slides - -#### Color Palette Selection - -**Choosing colors creatively**: -- **Think beyond defaults**: What colors genuinely match this specific topic? Avoid autopilot choices. -- **Consider multiple angles**: Topic, industry, mood, energy level, target audience, brand identity (if mentioned) -- **Be adventurous**: Try unexpected combinations - a healthcare presentation doesn't have to be green, finance doesn't have to be navy -- **Build your palette**: Pick 3-5 colors that work together (dominant colors + supporting tones + accent) -- **Ensure contrast**: Text must be clearly readable on backgrounds - -**Example color palettes** (use these to spark creativity - choose one, adapt it, or create your own): - -1. **Classic Blue**: Deep navy (#1C2833), slate gray (#2E4053), silver (#AAB7B8), off-white (#F4F6F6) -2. **Teal & Coral**: Teal (#5EA8A7), deep teal (#277884), coral (#FE4447), white (#FFFFFF) -3. **Bold Red**: Red (#C0392B), bright red (#E74C3C), orange (#F39C12), yellow (#F1C40F), green (#2ECC71) -4. **Warm Blush**: Mauve (#A49393), blush (#EED6D3), rose (#E8B4B8), cream (#FAF7F2) -5. **Burgundy Luxury**: Burgundy (#5D1D2E), crimson (#951233), rust (#C15937), gold (#997929) -6. **Deep Purple & Emerald**: Purple (#B165FB), dark blue (#181B24), emerald (#40695B), white (#FFFFFF) -7. **Cream & Forest Green**: Cream (#FFE1C7), forest green (#40695B), white (#FCFCFC) -8. **Pink & Purple**: Pink (#F8275B), coral (#FF574A), rose (#FF737D), purple (#3D2F68) -9. **Lime & Plum**: Lime (#C5DE82), plum (#7C3A5F), coral (#FD8C6E), blue-gray (#98ACB5) -10. **Black & Gold**: Gold (#BF9A4A), black (#000000), cream (#F4F6F6) -11. **Sage & Terracotta**: Sage (#87A96B), terracotta (#E07A5F), cream (#F4F1DE), charcoal (#2C2C2C) -12. **Charcoal & Red**: Charcoal (#292929), red (#E33737), light gray (#CCCBCB) -13. **Vibrant Orange**: Orange (#F96D00), light gray (#F2F2F2), charcoal (#222831) -14. **Forest Green**: Black (#191A19), green (#4E9F3D), dark green (#1E5128), white (#FFFFFF) -15. **Retro Rainbow**: Purple (#722880), pink (#D72D51), orange (#EB5C18), amber (#F08800), gold (#DEB600) -16. **Vintage Earthy**: Mustard (#E3B448), sage (#CBD18F), forest green (#3A6B35), cream (#F4F1DE) -17. **Coastal Rose**: Old rose (#AD7670), beaver (#B49886), eggshell (#F3ECDC), ash gray (#BFD5BE) -18. **Orange & Turquoise**: Light orange (#FC993E), grayish turquoise (#667C6F), white (#FCFCFC) - -#### Visual Details Options - -**Geometric Patterns**: -- Diagonal section dividers instead of horizontal -- Asymmetric column widths (30/70, 40/60, 25/75) -- Rotated text headers at 90° or 270° -- Circular/hexagonal frames for images -- Triangular accent shapes in corners -- Overlapping shapes for depth - -**Border & Frame Treatments**: -- Thick single-color borders (10-20pt) on one side only -- Double-line borders with contrasting colors -- Corner brackets instead of full frames -- L-shaped borders (top+left or bottom+right) -- Underline accents beneath headers (3-5pt thick) - -**Typography Treatments**: -- Extreme size contrast (72pt headlines vs 11pt body) -- All-caps headers with wide letter spacing -- Numbered sections in oversized display type -- Monospace (Courier New) for data/stats/technical content -- Condensed fonts (Arial Narrow) for dense information -- Outlined text for emphasis - -**Chart & Data Styling**: -- Monochrome charts with single accent color for key data -- Horizontal bar charts instead of vertical -- Dot plots instead of bar charts -- Minimal gridlines or none at all -- Data labels directly on elements (no legends) -- Oversized numbers for key metrics - -**Layout Innovations**: -- Full-bleed images with text overlays -- Sidebar column (20-30% width) for navigation/context -- Modular grid systems (3×3, 4×4 blocks) -- Z-pattern or F-pattern content flow -- Floating text boxes over colored shapes -- Magazine-style multi-column layouts - -**Background Treatments**: -- Solid color blocks occupying 40-60% of slide -- Gradient fills (vertical or diagonal only) -- Split backgrounds (two colors, diagonal or vertical) -- Edge-to-edge color bands -- Negative space as a design element - -### Layout Tips -**When creating slides with charts or tables:** -- **Two-column layout (PREFERRED)**: Use a header spanning the full width, then two columns below - text/bullets in one column and the featured content in the other. This provides better balance and makes charts/tables more readable. Use flexbox with unequal column widths (e.g., 40%/60% split) to optimize space for each content type. -- **Full-slide layout**: Let the featured content (chart/table) take up the entire slide for maximum impact and readability -- **NEVER vertically stack**: Do not place charts/tables below text in a single column - this causes poor readability and layout issues - -### Workflow -1. **MANDATORY - READ ENTIRE FILE**: Read [`html2pptx.md`](html2pptx.md) completely from start to finish. **NEVER set any range limits when reading this file.** Read the full file content for detailed syntax, critical formatting rules, and best practices before proceeding with presentation creation. -2. Create an HTML file for each slide with proper dimensions (e.g., 720pt × 405pt for 16:9) - - Use `

`, `

`-`

`, `