diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/.gitignore b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/.gitignore new file mode 100644 index 00000000..344d2bd1 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/.gitignore @@ -0,0 +1,53 @@ + +.DS_Store + +# AWS CDK +cdk.out/ + +# Node.js +node_modules/ +npm-debug.log +yarn-error.log + +# TypeScript +*.tsbuildinfo +dist/ +out/ +build/ + +# Logs +logs/ +*.log + +# Dependency directories +jspm_packages/ +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# MacOS +.DS_Store + +# IDEs and editors +.vscode/ +.idea/ +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Environment variables +.env +.env.local +.env.*.local + +# Coverage directory used by tools like istanbul +coverage/ \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/README.md b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/README.md new file mode 100644 index 00000000..f826db78 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/README.md @@ -0,0 +1,230 @@ +# **RAG Pipeline with CI/CD** + +This project implements a comprehensive **CI/CD pipeline** for an end-to-end **Retrieval-Augmented Generation (RAG)** system powered by **Amazon Bedrock Knowledge Bases** and **AWS CDK**. The pipeline automates the deployment and management of key AWS resources critical for the RAG architecture. At its core, the solution integrates **Amazon Bedrock's foundation models and Knowledge Bases** with essential AWS services such as S3, Lambda, DynamoDB, and Step Functions to manage data ingestion, transformation, and retrieval. Through predefined stages spanning **QA and Production environments**, raw data flows through transformation pipelines, where it is processed, evaluated, and optimized for enhanced knowledge retrieval. + +The solution leverages **AWS CodePipeline** to streamline multi-stage deployments, automating infrastructure updates from code commits to production rollouts. RAG evaluations act as critical checkpoints to ensure system integrity by validating both **data and code changes**. Promotions from QA to Production are only allowed after successful RAG evaluations, ensuring that only validated changes make it to production. The architecture offers **fine-grained control** over the deployment lifecycle, with **manual/automatic approvals** and **state machine-driven workflows** managing critical decisions. By combining **CI/CD best practices with RAG workflows**, the project provides a scalable and automated framework to continuously deploy GenAI-powered applications, leveraging real-time external knowledge. This solution accelerates time-to-market by providing a production-ready framework for enterprises building AI-powered applications with Amazon Bedrock. + +## **Accessing the Example Streamlit App** + +The **Streamlit app** deployed as part of this project serves as an **example interface** to demonstrate the capabilities of the **RAG pipeline**. + +1. **Go to the AWS CloudFormation Console**: + - In the list of stacks, locate the **Web App Stack** (e.g., `Prod-WebAppStack`). + +2. **Find the Web App Endpoint**: + - Click on the **`Prod-WebAppStack`**. + - In the **"Outputs" tab**, look for the key labeled **`AlbDnsName`**. + - This value provides the **DNS name** of the Application Load Balancer (ALB) hosting the example Streamlit app. + +3. **Access the App**: + - Click on the **AlbDnsName** or copy the link and open it in a browser. + - The Streamlit app should load, allowing you to explore the chat functionality. + + +## Solution Architecture + +### Data Ingestion Workflow +**Data Ingestion Workflow** is designed to handle and process raw data files uploaded to the QA environment, transforming them into a format ready for ingestion into the Knowledge Base. This workflow is highly adaptable, enabling customers to integrate their own data transformation and validation processes based on their unique requirements. **_Note_**: The data transformation step is provided as a flexible component, allowing customers to apply any necessary data processing, such as file format conversions, text extraction, or data normalization, before ingestion. This approach ensures that the Knowledge Base receives high-quality, structured data suitable for retrieval and analysis. + +![alt text](image-1.png) + +1. **Raw Files Upload (Starting Point)** + - **Raw Files S3 bucket** is the starting point where raw files are uploaded. Each file upload or deletion in this bucket triggers an S3 event, setting off the data ingestion process. + +2. **Event Capture and Workflow Triggering** + - **EventBridge rule** captures specific S3 events (file upload or delete) and triggers an AWS Step Function, which is responsible for coordinating the data transformation workflow. This rule ensures that each file upload or deletion event invokes the data transformation step function to handle the event. + +3. **Data Transformation Process (AWS Step Function Workflow)** + - **AWS Step Function** orchestrates the data transformation process with the following steps: + - **S3 Event Processor Lambda function** extracts relevant details from each S3 event. Based on the event type (upload or deletion), it sends a message to one of two **Amazon SQS queues**: + - **File Upload Queue**: For upload events, a message is added to signal that a new file requires processing. + - **File Deletion Queue**: For delete events, a message is added to signal that a file has been removed. + - **File Processor Lambda function** is triggered whenever a message appears in either SQS queue: + - **For upload events**: The function processes the raw file, applies necessary transformations, and saves the output in the **Processed Files S3 bucket**. Transformation requirements may vary depending on specific use cases. For instance, if you use the advanced parsing feature of the Knowledge Base, which currently supports only PDF format, files must be converted to PDF before ingesting the file. + - **For deletion events**: The function deletes the corresponding transformed file from the **Processed Files S3 bucket** to maintain consistency and keep the processed data aligned with the raw data. + +4. **Scheduled Knowledge Base Ingestion** + - **Amazon EventBridge Scheduler** is configured to run every 2 minutes, triggering the **Knowledge Base Ingestion Step Function** workflow. This schedule ensures that data ingestion into the Knowledge Base is consistent and near real-time, though the interval can be adjusted based on customer requirements (e.g., every hour, several times a day, etc.). + +5. **Knowledge Base Ingestion Workflow (Step Functions)** + - **Knowledge Base Ingestion Step Function** orchestrates the data ingestion process into the Amazon Bedrock Knowledge Base with the following steps: + - **Check For New Files Lambda function** scans the **Metadata Table** for newly processed files that need ingestion. + - If new files are identified, the **Start KB Ingestion Lambda function** initiates the ingestion process by calling Amazon Bedrock’s **StartIngestionJob** API. + - **Check KB Ingestion Status Lambda function** periodically checks the status of the ingestion job via the **GetIngestionJobStatus** API. + - **Retry Logic**: If the ingestion job is still in progress, the workflow waits for a specified interval before checking again. This ensures that the ingestion process completes successfully without unnecessary resource consumption. + - Upon successful completion of the ingestion job, the **Trigger RAG Evaluation Lambda function** is invoked to launch a **RAG Evaluation Step Function** workflow. This separate workflow validates the ingested data as part of the QA process. + - Finally, the workflow updates the **Metadata Table** with the final status of the ingestion job, ensuring that the table always reflects the most current state of each file and ingestion job. + +6. **File Metadata Table for Tracking and Status** + - **File Metadata Table** (Amazon DynamoDB) stores metadata and ingestion status for each file. This table is essential for tracking files through the entire ingestion pipeline—from their raw state in the S3 bucket, through processing, and finally to ingestion in the Knowledge Base. + + + +### **RAG Evaluation Workflow** + +--- +**RAG Evaluation Workflow** is designed to evaluate code and data changes in the QA environment to ensure that only high-quality, validated updates are promoted to production. This workflow is flexible, allowing customers to integrate their own RAG evaluation processes based on their specific requirements. **_Note_**: The RAG evaluation step is provided as a **placeholder** without a prescribed methodology, allowing customers to define and integrate any RAG evaluation approach that fits their needs + +![alt text](image-2.png) +### **1. Handling Code Changes** + +- **Code Changes Detection and CodePipeline Execution**: + - When **code changes** are detected (e.g., new features, bug fixes, or infrastructure updates), **AWS CodePipeline** is triggered automatically. CodePipeline manages the deployment of code changes across different environments, including QA and Production. + +- **Post-QA Stage and RAG Evaluation**: + - As part of the CodePipeline workflow, there is a **Post-QA Stage** where the RAG Evaluation takes place. This stage runs the **RAG Evaluation Step Function** to assess the quality of code changes. + - The **RAG Evaluation Lambda function** is invoked as part of this process, which can be customized to implement any evaluation logic. A placeholder example evaluates based on a defined success rate threshold (e.g., 80%). + +- **Automated Approval Process in CodePipeline**: + - If the RAG Evaluation is **successful** (e.g., meets the required success threshold), the Step Function automatically triggers the **approval process** within CodePipeline. + - This automatic approval allows the CodePipeline to proceed with deploying the code changes from QA to Production. + - If the evaluation **fails**, the pipeline halts, requiring intervention to resolve issues before reattempting deployment. + +#### **Example Placeholder Implementation for Code Changes** +```typescript +// Example Lambda function that simulates a RAG evaluation process +const ingestionResult = { successRate: 85 }; // Simulating a success rate +const threshold = 80; // Threshold for passing +const ragStatus = ingestionResult.successRate >= threshold ? 'PASSED' : 'FAILED'; +``` +This example checks if the success rate meets the threshold. Customers can replace this logic with their own evaluation methodology. + +--- + +### **2. Handling Data Changes** + +- **Data Changes Detection and RAG Evaluation Trigger**: + - When **data changes** occur (new or updated data becomes available in the QA environment), the **Knowledge Base Ingestion Step Function Workflow** detects these changes and triggers the **Invoke RAG Evaluation Step Function** as part of the ingestion pipeline. + +- **RAG Evaluation for Data Changes**: + - The **RAG Evaluation Step Function** assesses the new data to ensure quality and compliance. This may include checks such as schema validation, data consistency, and adherence to business rules. + - Customers can customize this evaluation to fit their data validation needs, using the placeholder as a baseline or replacing it entirely with their own logic. + +- **Automatic Data Promotion to Production**: + - **No Manual Approval Required**: Unlike code changes, data changes do not require manual approval, enabling faster and automated propagation of data updates. + - **Invoke QA to Prod Step Function**: Upon a successful RAG evaluation, the Step Function triggers the **Invoke QA to Prod Step Function**, which initiates the **Move Files State Machine**. + - **Data Transfer Execution**: This state machine orchestrates the transfer of validated data from the QA S3 bucket to the Production S3 bucket, completing the data promotion process. + +--- + +## **cdk.json: Configuration for Your CDK Project** + +The **`cdk.json`** file defines the configuration settings and context values for the CDK project. It ensures the CDK framework knows how to execute the app and what environment-specific settings to apply. Below is the content of the `cdk.json` file: + +```json +{ + "app": "npx ts-node bin/main.ts", + "context": { + "defaultProject": "rag-project", + "defaultRegion": "us-east-1", + "defaultAccount": "xxxxxxx", + "defaultEnvironment": "QA", + "prodAccount": "xxxxxxx", + "prodRegion": "us-west-2", + "bedrockModelID": "anthropic.claude-3-haiku-20240307-v1:0", + "@aws-cdk/customresources:installLatestAwsSdkDefault": false + } +} +``` + +### **Explanation of Key Fields in `cdk.json`:** + +- **`app`**: Specifies the command to run the CDK app. Here, **`npx ts-node bin/main.ts`** runs the TypeScript entry point directly using `ts-node`. +- **`context`**: Stores environment-specific configurations and variables: + - **`defaultProject`**: Name of the project + - **`defaultRegion`**: The AWS region for QA deployments + - **`defaultAccount`**: The AWS account ID used for deployments. + - **`defaultEnvironment`**: Indicates the default environment as **QA**. + - **`prodAccount`** and **`prodRegion`**: AWS account and region for production + - **`bedrockModelID`**: The foundation model ID used in Amazon Bedrock. + - **`installLatestAwsSdkDefault`**: Controls whether the latest AWS SDK is installed for custom resources. + +The `cdk.json` file ensures the CDK app deploys correctly across different stages by centralizing environment variables and configurations. This makes the pipeline flexible and easy to extend for multiple environments (e.g., QA, Prod). + +--- + +## **Prerequisites** + +Ensure the following tools are installed: + +1. **Node.js**: [Download here](https://nodejs.org). +2. **AWS CDK CLI**: Install the AWS CDK CLI globally: + ```bash + npm install -g aws-cdk + ``` +3. **AWS CLI**: [Install and configure](https://aws.amazon.com/cli/) the AWS CLI. + +--- + +## **Installation** + +1. **Navigate to the Project Folder**: + ```bash + cd path/to/RAG-PIPELINE-WITH-CICD + ``` + +2. **Install Dependencies**: + ```bash + npm install + ``` + +3. **Build the Project**: + ```bash + npm run build + ``` + +4. **Synthesize the CDK App**: + ```bash + npx cdk synth + ``` + +5. **Deploy the CDK App**: + ```bash + npx cdk deploy --all + ``` + +--- + +## **Project Structure** + +- **`bin/`**: Contains the entry point for the CDK app. +- **`lib/`**: Contains the core CDK stack definitions. + - **`constructs/`**: Custom reusable components. + - **`stacks/`**: Defines the various AWS resources. + - **`stages/`**: Pipeline stages (e.g., QA, Prod). +- **`src/`**: Lambda function source code. +- **`cdk.json`**: CDK configuration file. +- **`package.json`**: Node.js dependencies and scripts. +- **`tsconfig.json`**: TypeScript configuration. + +--- + +## **Troubleshooting Tips** + +- **AWS CLI Credentials**: Ensure AWS credentials are configured: + ```bash + aws configure + ``` +- **TypeScript Issues**: Install TypeScript if needed: + ```bash + npm install -D typescript + ``` +- **CDK CLI Version**: If issues persist, try updating the CDK CLI: + ```bash + npm install -g aws-cdk@latest + ``` + + +--- + +## **Useful Commands** + +- **Build**: `npm run build` +- **Synthesize**: `npx cdk synth` +- **Deploy**: `npx cdk deploy` +- **Destroy**: `npx cdk destroy` + + +--- + +This RAG pipeline enables fast and efficient management of GenAI applications, ensuring smooth integration of data and code changes into production with the power of **Amazon Bedrock** and AWS CDK. diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/bin/main.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/bin/main.ts new file mode 100644 index 00000000..06806976 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/bin/main.ts @@ -0,0 +1,16 @@ +import { App } from "aws-cdk-lib"; +import { CodePipelineStack } from "../lib/stacks/CodePipelineStack"; + + +const app = new App(); + + +new CodePipelineStack(app, "CodePipelineStack", { + env: { + account: process.env.CDK_DEFAULT_ACCOUNT as string, + region: process.env.CDK_DEFAULT_REGION as string, + }, + codePipelineName: "RAGPipeline", +}); + + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.context.json b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.context.json new file mode 100644 index 00000000..3682de4b --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.context.json @@ -0,0 +1,21 @@ +{ + "availability-zones:account=533267284022:region=us-east-1": [ + "us-east-1a", + "us-east-1b", + "us-east-1c", + "us-east-1d", + "us-east-1e", + "us-east-1f" + ], + "availability-zones:account=533267284022:region=us-east-2": [ + "us-east-2a", + "us-east-2b", + "us-east-2c" + ], + "availability-zones:account=533267284022:region=us-west-2": [ + "us-west-2a", + "us-west-2b", + "us-west-2c", + "us-west-2d" + ] +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.json b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.json new file mode 100644 index 00000000..1340f214 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.json @@ -0,0 +1,13 @@ +{ + "app": "npx ts-node bin/main.ts", + "context": { + "defaultProject": "rag-project", + "defaultRegion": "us-east-1", + "defaultAccount": "533267284022", + "defaultEnvironment": "QA", + "prodAccount": "533267284022", + "prodRegion": "us-west-2", + "bedrockModelID": "anthropic.claude-3-haiku-20240307-v1:0", + "@aws-cdk/customresources:installLatestAwsSdkDefault": false + } +} \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/image-1.png b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/image-1.png new file mode 100644 index 00000000..1fb11f75 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/image-1.png differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/image-2.png b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/image-2.png new file mode 100644 index 00000000..d7ffea54 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/image-2.png differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/constructs/custom-s3-data-source.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/constructs/custom-s3-data-source.ts new file mode 100644 index 00000000..40edb9fb --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/constructs/custom-s3-data-source.ts @@ -0,0 +1,59 @@ +import { Construct } from 'constructs'; +import { bedrock } from '@cdklabs/generative-ai-cdk-constructs'; +import { aws_bedrock as bedrockL1 } from 'aws-cdk-lib'; +import { IBucket } from 'aws-cdk-lib/aws-s3'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; + +export interface CustomS3DataSourceProps extends bedrock.S3DataSourceProps { + intermediateBucket: IBucket; + transformationLambda: IFunction; +} + +export class CustomS3DataSource extends Construct { + public readonly dataSourceId: string; + + constructor(scope: Construct, id: string, props: CustomS3DataSourceProps) { + super(scope, id); + + const customTransformationConfiguration = { + intermediateStorage: { + s3Location: { + uri: `s3://${props.intermediateBucket.bucketName}/` + } + }, + transformations: [ + { + stepToApply: 'POST_CHUNKING', // Custom chunking logic applied after files are processed by Bedrock + transformationFunction: { + transformationLambdaConfiguration: { + lambdaArn: props.transformationLambda.functionArn // Custom Lambda function for chunking, and other transformations + } + } + } + ] + }; + + // Use 'NONE' chunking strategy so Bedrock treats each file as a chunk + const kbS3DataSource = new bedrockL1.CfnDataSource(this, 'MyCfnDataSource', { + knowledgeBaseId: props.knowledgeBase.knowledgeBaseId, + name: props.dataSourceName, + dataSourceConfiguration: { + type: 'S3', + s3Configuration: { + bucketArn: props.bucket.bucketArn, + }, + }, + vectorIngestionConfiguration: { + chunkingConfiguration: { + chunkingStrategy: 'NONE', // Use 'NONE' since custom chunking is handled by the Lambda function + }, + customTransformationConfiguration, + } + }); + + // attrDataSourceId is a property of the CfnDataSource construct that represents the unique identifier of the data source + // Extract the dataSourceId from the CfnDataSource construct and assign it to the dataSourceId property of the CustomS3DataSource construct + // Thats why 'this' is used to refer to the current instance of the CustomS3DataSource construct + this.dataSourceId = kbS3DataSource.attrDataSourceId; // Expose the dataSourceId as a public property + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/BedrockStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/BedrockStack.ts new file mode 100644 index 00000000..978b97ef --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/BedrockStack.ts @@ -0,0 +1,242 @@ +import { CfnOutput, Duration, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { bedrock } from "@cdklabs/generative-ai-cdk-constructs"; +import { BlockPublicAccess, Bucket, IBucket } from "aws-cdk-lib/aws-s3"; +import { Runtime, Code, Function, LayerVersion } from "aws-cdk-lib/aws-lambda"; +import { CustomS3DataSource } from "../constructs/custom-s3-data-source"; +import { join } from "path"; +import { Effect, ManagedPolicy, Policy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { StringParameter } from "aws-cdk-lib/aws-ssm"; +import { SSMParameterReader } from "./ssm-parameter-reader"; + +export interface BedrockStackProps extends StackProps { + stageName: string; + codePipelineName: string; +} +/** + * ## BedrockStack Overview + * + * ### Usage: + * This stack sets up the core resources required for the RAG pipeline, integrating with Amazon Bedrock Knowledge bases. + * It provisions Lambda functions, S3 buckets, IAM roles, and policies to manage the ingestion, transformation, and retrieval of data. + * + * ### Key Features: + * - Amazon Bedrock Integration: Configures Bedrock knowledge bases, data sources and custom chunking Lambda functions. + * - Lambda Function for Data Transformation: Handles data chunking and transformation for ingestion. + * - S3 Buckets: Stores intermediate data and documents for knowledge ingestion. + * - IAM Policies: Grants secure access to Lambda functions and Bedrock models. + * - SSM Parameter Store Integration: Retrieves configuration values dynamically for different environments (QA/Prod). + */ + + +export class BedrockStack extends Stack { + public knowledgeBaseId!: string; // Store the Knowledge Base ID + public dataSourceId!: string; // Store the Data Source ID + private s3AccessPolicy!: Policy; // Store the S3 access policy as a class property + + constructor(scope: Construct, id: string, props: BedrockStackProps) { + super(scope, id, props); + + // Create a Lambda execution role with basic execution permissions + const customLambdaRole = new Role(this, "CustomChunkingLambdaRole", { + assumedBy: new ServicePrincipal("lambda.amazonaws.com"), // Lambda service principal + managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole")], + }); + + // Retrieve account number and region from environment variables + const accountNumber = process.env.CDK_DEFAULT_ACCOUNT; + const region = this.region; + + // Ensure account number and region are provided, otherwise throw an error + if (!accountNumber || !region) { + throw new Error("CDK_DEFAULT_ACCOUNT or AWS_REGION environment variable is not set"); + } + + // Set the foundation model ID + const foundationModelID = "anthropic.claude-3-sonnet-20240229-v1:0"; + + // Determine the appropriate S3 bucket name based on the stage (QA or Prod) + let docBucketName = props?.stageName === "QA" + ? "processed-data-source-bucket-533267284022-qa" + : "processed-data-source-bucket-533267284022-prod"; + + // Reference an existing S3 bucket by name + const docBucket = Bucket.fromBucketName(this, "DocBucket", docBucketName); + // console.log("Processed Files S3 Data Source: ", docBucketName); // Log the selected bucket name + + + // // Read the S3 bucket name from the SSM parameter store (the buckets are created as part of the data ingestion stack) + // const docBucketName = StringParameter.fromStringParameterName(this, 'ProcessedS3BucketName', `/${props.codePipelineName}/${props.stageName}/processed-s3-data-source`).stringValue; + + // // Reference an existing S3 bucket by name + // const docBucket = Bucket.fromBucketName(this, "DocBucket", docBucketName); + + // Create a new S3 bucket to store intermediate data + const intermediateBucket = new Bucket(this, "IntermediateBucket", { + blockPublicAccess: BlockPublicAccess.BLOCK_ALL, // Ensure no public access + removalPolicy: RemovalPolicy.DESTROY, // Remove the bucket when the stack is deleted + autoDeleteObjects: true, // Automatically delete objects when bucket is removed + }); + + // Initialize the S3 access policy with permissions to access specific S3 buckets + this.s3AccessPolicy = new Policy(this, "AmazonBedrockS3PolicyForKnowledgeBase", { + policyName: "AmazonBedrockS3PolicyForKnowledgeBase", + statements: [ + new PolicyStatement({ + sid: "S3ListBucketStatement", // Allow listing objects in the bucket + effect: Effect.ALLOW, + actions: ["s3:ListBucket"], + resources: [docBucket.bucketArn], + conditions: { StringEquals: { "aws:ResourceAccount": accountNumber } }, + }), + new PolicyStatement({ + sid: "S3GetObjectStatement", // Allow reading objects from buckets + effect: Effect.ALLOW, + actions: ["s3:GetObject"], + resources: [ + docBucket.bucketArn, `${docBucket.bucketArn}/*`, + intermediateBucket.bucketArn, `${intermediateBucket.bucketArn}/*` + ], + conditions: { StringEquals: { "aws:ResourceAccount": accountNumber } }, + }), + new PolicyStatement({ + sid: "S3PutObjectStatement", // Allow writing objects to intermediate bucket + effect: Effect.ALLOW, + actions: ["s3:PutObject"], + resources: [`${intermediateBucket.bucketArn}/*`], + conditions: { StringEquals: { "aws:ResourceAccount": accountNumber } }, + }), + ], + }); + + // Retrieve the custom Lambda package bucket name from the SSM parameter store + let bedrockCustomLambdaBucketName: string; + if (props.stageName === "QA") { + bedrockCustomLambdaBucketName = StringParameter.valueForStringParameter(this, '/RAGPipeline/PreQABucketSetupStage/lambda-package-bucket-name'); + // console.log("Bedrock Custom Lambda Package Bucket Name: ", bedrockCustomLambdaBucketName); // Log the bucket name + } else { + bedrockCustomLambdaBucketName = StringParameter.valueForStringParameter(this, '/RAGPipeline/PreProdBucketSetupStage/lambda-package-bucket-name'); + // console.log("Bedrock Custom Lambda Package Bucket Name: ", bedrockCustomLambdaBucketName); // Log the bucket name + } + + + + // Add logging to capture the parameter value + new CfnOutput(this, 'BedrockCustomLambdaPackageBucketNameOutput', { + value: bedrockCustomLambdaBucketName, + description: 'The name of the S3 bucket for the custom Lambda package', + }); + + const bedrockCustomLambdaPackageBucket = Bucket.fromBucketName(this, 'BedrockCustomLambdaPackageBucket', bedrockCustomLambdaBucketName); + + + // Create the Lambda function responsible for transforming data + const transformationLambda = this.createTransformationLambda(customLambdaRole, bedrockCustomLambdaPackageBucket); + + // Configure policies and link the resources created above + this.configurePolicies(transformationLambda, accountNumber, region, foundationModelID, docBucket, intermediateBucket); + } + + // Method to create the Lambda function for data transformation + private createTransformationLambda(customLambdaRole: Role, bedrockCustomLambdaPackageBucket: IBucket): Function { + // const lambdaCodePath = join(__dirname, "..", "..", "src", "services", "tmp", "custom_chunking_lambda_package.zip"); + // console.log(`Using Lambda code from: ${lambdaCodePath}`); + + // Define the Lambda function with necessary properties + const transformationLambda = new Function(this, "CustomChunkingLambda", { + runtime: Runtime.PYTHON_3_12, // Use Python 3.12 runtime + // code: Code.fromAsset(lambdaCodePath), // Reference the pre-packaged Lambda code + code: Code.fromBucket(bedrockCustomLambdaPackageBucket, 'custom_chunking_lambda_package.zip'), + handler: "custom_chunking_lambda_function.lambda_handler", // Lambda function handler + role: customLambdaRole, // Assign the Lambda execution role + timeout: Duration.minutes(15), // Set timeout to 15 minutes + memorySize: 10240, // Allocate 10 GB memory + // Removed the Pillow layer as it is not required for the custom Lambda function since image processing is not needed + // layers: [ + // LayerVersion.fromLayerVersionArn( + // this, "PillowLayer", `arn:aws:lambda:${this.region}:770693421928:layer:Klayers-p312-pillow:1` + // ), + // ], + }); + + return transformationLambda; + } + + // Method to configure policies and link Lambda with other resources + private configurePolicies( + transformationLambda: Function, accountNumber: string, region: string, foundationModelID: string, + docBucket: IBucket, intermediateBucket: Bucket + ) { + // Policy to allow invoking the Lambda function + const lambdaInvokePolicy = new Policy(this, "AmazonBedrockLambdaPolicyForKnowledgeBase", { + policyName: "AmazonBedrockLambdaPolicyForKnowledgeBase", + statements: [ + new PolicyStatement({ + sid: "LambdaInvokeFunctionStatement", + effect: Effect.ALLOW, + actions: ["lambda:InvokeFunction"], + resources: [`${transformationLambda.functionArn}:*`], + conditions: { StringEquals: { "aws:ResourceAccount": accountNumber } }, + }), + ], + }); + + // Policy to allow invoking Bedrock foundation models + const foundationalModelInvokePolicy = new Policy(this, "AmazonBedrockFoundationalModelInvokePolicy", { + policyName: "AmazonBedrockFoundationalModelInvokePolicy", + statements: [ + new PolicyStatement({ + sid: "BedrockInvokeModelStatement", + effect: Effect.ALLOW, + actions: ["bedrock:InvokeModel"], + resources: [`arn:aws:bedrock:${region}::foundation-model/${foundationModelID}`], + }), + ], + }); + + // Policy to allow retrieving Lambda layer versions + const layerArn = `arn:aws:lambda:${region}:770693421928:layer:Klayers-p312-pillow:1`; + const getLayerVersionPolicy = new Policy(this, "GetLayerVersionPolicy", { + policyName: "GetLayerVersionPolicy", + statements: [ + new PolicyStatement({ + sid: "GetLayerVersionStatement", + effect: Effect.ALLOW, + actions: ["lambda:GetLayerVersion"], + resources: [layerArn], + }), + ], + }); + + // Attach policies to the Lambda function role + transformationLambda.role?.attachInlinePolicy(lambdaInvokePolicy); + transformationLambda.role?.attachInlinePolicy(this.s3AccessPolicy); + transformationLambda.role?.attachInlinePolicy(foundationalModelInvokePolicy); + transformationLambda.role?.attachInlinePolicy(getLayerVersionPolicy); + + // Create a KnowledgeBase and attach necessary policies + const kb = new bedrock.KnowledgeBase(this, "KnowledgeBase", { + embeddingsModel: bedrock.BedrockFoundationModel.TITAN_EMBED_TEXT_V2_1024, + instruction: "Use this knowledge base to answer questions about a wide range of topics.", + }); + + kb.role.attachInlinePolicy(lambdaInvokePolicy); + kb.role.attachInlinePolicy(this.s3AccessPolicy); // Allow KB to access S3 buckets + kb.role.attachInlinePolicy(foundationalModelInvokePolicy); // Allow KB to invoke foundational models + + // Create a knowledge base data source and attach necessary policies + // CustomS3DataSource is a custom construct that extends the native S3DataSource L1 construct to include additional properties such as intermediateBucket and transformationLambda + const kbS3DataSource = new CustomS3DataSource(this, "KBS3DataSource", { + bucket: docBucket, + knowledgeBase: kb, + dataSourceName: "finance", + chunkingStrategy: bedrock.ChunkingStrategy.NONE, + intermediateBucket: intermediateBucket, + transformationLambda: transformationLambda, + }); + + // Store the IDs for further reference + this.knowledgeBaseId = kb.knowledgeBaseId; + this.dataSourceId = kbS3DataSource.dataSourceId; + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/CodePipelineStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/CodePipelineStack.ts new file mode 100644 index 00000000..6a9e6819 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/CodePipelineStack.ts @@ -0,0 +1,282 @@ + +import { Duration, Fn, Stack, StackProps } from "aws-cdk-lib"; +import { CodeBuildStep, CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep } from "aws-cdk-lib/pipelines"; +import { Construct } from "constructs"; +import { CodePipelineStage } from "../stages/CodePipelineStage"; +import { ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { LinuxBuildImage } from "aws-cdk-lib/aws-codebuild"; +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; +import { Runtime } from "aws-cdk-lib/aws-lambda"; +import { join } from "path"; +import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources"; + +interface CodePipelineStackProps extends StackProps { + env: { + account: string; + region: string; + }; + codePipelineName: string; +} + +/** + * ## CodePipelineStack Overview + * + * ### Usage: + * This stack defines the CI/CD pipeline that automates the deployment of the RAG system across multiple environments (QA and Production). + * It uses AWS CodePipeline, CodeBuild, and approval steps to manage infrastructure updates and deployments. + * + * ### Key Features: + * - CodePipeline Integration: Automates the build, test, and deployment process for each environment. + * - CodeBuild Step: Packages Lambda functions and builds the infrastructure using CDK. + * - Manual Approval Workflow: Ensures that only validated code and data changes are promoted to production. + * - SSM Parameter Store: Stores configuration values like bucket names and pipeline parameters. + * - Multi-Environment Support: Deploys and promotes resources across QA and Production environments. + */ + + +export class CodePipelineStack extends Stack { + constructor(scope: Construct, id: string, props: CodePipelineStackProps) { + super(scope, id, props); + + // Initialize the pipeline + const cicdPipeline = new CodePipeline(this, "RAGPipeline", { + pipelineName: props.codePipelineName, + selfMutation: true, + role: new Role(this, "CodePipelineRole", { + assumedBy: new ServicePrincipal("codepipeline.amazonaws.com"), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess"), + ], + }), + synth: new ShellStep("Synth", { + input: CodePipelineSource.gitHub( + "manoj-selvakumar5/amazon-bedrock-samples", + "rag-cicd-without-image-processing", + ), + commands: [ + "echo 'Current working directory:' $(pwd)", + "ls -ltr", + "cd rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd", + "echo 'New working directory:' $(pwd)", + "ls -ltr", + "npm ci", + "npm run build", + "npx cdk synth" + ], + primaryOutputDirectory: 'rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/cdk.out' // Updated to reflect the correct project root directory + }), + dockerEnabledForSynth: true, + }); + + // Define the role with required policies + const codeBuildRole = new Role(this, 'CodeBuildRole', { + assumedBy: new ServicePrincipal('codebuild.amazonaws.com'), + managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess')], + }); + + // Add specific permission for SSM GetParameter + codeBuildRole.addToPolicy(new PolicyStatement({ + actions: ['ssm:GetParameter'], + resources: [ + // `arn:aws:ssm:${this.region}:${this.account}:parameter/${props.codePipelineName}/PreQABucketSetupStage/lambda-package-bucket-name`, + `arn:aws:ssm:${this.region}:${this.account}:parameter/${props.codePipelineName}/*`, + ], + })); + + + // Add the S3 Bucket Stage + const preQABucketSetupStage = cicdPipeline.addStage( + new CodePipelineStage(this, 'PreQABucketSetupStage', { + stageName: 'PreQABucketSetupStage', + env: { + account: this.node.tryGetContext('defaultAccount'), // Retrieve a value from the CDK application context + region: this.node.tryGetContext('defaultRegion'), + }, + codePipelineName: props.codePipelineName, + }) + ); + + + + // Add Lambda Build Step to QA Stage + const qaStage = cicdPipeline.addStage( + new CodePipelineStage(this, "QA", { + stageName: "QA", + env: { + account: this.node.tryGetContext("defaultAccount"), + region: this.node.tryGetContext("defaultRegion"), + }, + codePipelineName: props.codePipelineName, + }), + { + pre: [ + // Pre-deployment Step: Build Lambda Package using CodeBuild required for Custom Lambda parser used in Bedrock + new CodeBuildStep("BuildLambdaPackage", { + commands: [ + "echo 'Current working directory:' $(pwd)", + "ls -R", + "chmod +x rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh", // Make the script executable + "./rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh" // Run the script + ], + + role: codeBuildRole, + buildEnvironment: { + buildImage: LinuxBuildImage.STANDARD_5_0, // Use standard CodeBuild image + environmentVariables: { + CODE_PIPELINE_NAME: { value: props.codePipelineName }, + STAGE_NAME: { value: "PreQABucketSetupStage" }, // To retrieve the bucket name from SSM + AWS_REGION: { value: this.node.tryGetContext("defaultRegion") }, // Pass the region as an environment variable + }, + }, + }), + ], + } + ); + + + // New Post-QA Stage: Handles RAG evaluation and manual approval + const postQAApprovalStage = cicdPipeline.addStage( + new CodePipelineStage(this, "PostQAApproval", { + stageName: "PostQAApproval", + env: { + account: this.node.tryGetContext("defaultAccount"), + region: this.node.tryGetContext("defaultRegion"), + }, + codePipelineName: props.codePipelineName, + }) + ); + + postQAApprovalStage.addPost( + new CodeBuildStep("TriggerRAGEvaluationAfterQADeployment", { + commands: [ + // Retrieve the state machine ARN from SSM in the us-east-1 region + `aws stepfunctions start-execution --region ${this.node.tryGetContext("defaultRegion")} --state-machine-arn $(aws ssm get-parameter --name /${props.codePipelineName}/PostQAApproval/rag-evaluation-state-machine-arn --region ${this.node.tryGetContext("defaultRegion")} --query "Parameter.Value" --output text) --input '{}'`, + ], + buildEnvironment: { + buildImage: LinuxBuildImage.STANDARD_5_0, + }, + rolePolicyStatements: [ + new PolicyStatement({ + actions: ['ssm:GetParameter', 'states:StartExecution'], + resources: [ + `arn:aws:ssm:${this.node.tryGetContext("defaultRegion")}:${this.account}:parameter/${props.codePipelineName}/PostQAApproval/rag-evaluation-state-machine-arn`, + `arn:aws:states:${this.node.tryGetContext("defaultRegion")}:${this.account}:stateMachine:*`, + ], + }), + ], + }) + ); + + postQAApprovalStage.addPost(new ManualApprovalStep("ManualApprovalForProduction")); + + + + + // Add the S3 Bucket Stage + const preProdBucketSetupStage = cicdPipeline.addStage( + new CodePipelineStage(this, 'PreProdBucketSetupStage', { + stageName: 'PreProdBucketSetupStage', + env: { + account: this.node.tryGetContext('prodAccount'), + region: this.node.tryGetContext('prodRegion'), + }, + codePipelineName: props.codePipelineName, + }) + ); + + + // Prod Stage + const prodStage = cicdPipeline.addStage( + new CodePipelineStage(this, "Prod", { + stageName: "Prod", + env: { + account: this.node.tryGetContext("prodAccount"), + region: this.node.tryGetContext("prodRegion"), + }, + codePipelineName: props.codePipelineName, + }), + { + pre: [ + // Pre-deployment Step: Build Lambda Package for Prod Stage + new CodeBuildStep("BuildLambdaPackageForProd", { + commands: [ + "echo 'Current working directory:' $(pwd)", + "ls -R", + "chmod +x rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh", // Make the script executable + "./rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh" // Run the script + ], + role: codeBuildRole, // Use the shared CodeBuild role + buildEnvironment: { + buildImage: LinuxBuildImage.STANDARD_5_0, // Use standard CodeBuild image + environmentVariables: { + CODE_PIPELINE_NAME: { value: props.codePipelineName }, + STAGE_NAME: { value: "PreProdBucketSetupStage" }, // Specify the stage name for prod + AWS_REGION: { value: this.node.tryGetContext("prodRegion") }, // Pass the region as an environment variable + }, + }, + }), + ], + post: [ + // Post-deployment step: Trigger Copy Files State Machine + new CodeBuildStep("TriggerCopyFilesFromQAToProdStateMachine", { + commands: [ + `aws stepfunctions start-execution --region ${this.node.tryGetContext("prodRegion")} --state-machine-arn $(aws ssm get-parameter --name /${props.codePipelineName}/Prod/move-files-state-machine-arn --region ${this.node.tryGetContext("prodRegion")} --query "Parameter.Value" --output text) --input '{}'`, + ], + buildEnvironment: { + buildImage: LinuxBuildImage.STANDARD_5_0, + }, + rolePolicyStatements: [ + new PolicyStatement({ + actions: ["ssm:GetParameter", "states:StartExecution"], + resources: [ + `arn:aws:ssm:${this.node.tryGetContext("prodRegion")}:${this.account}:parameter/${props.codePipelineName}/Prod/move-files-state-machine-arn`, + `arn:aws:states:${this.node.tryGetContext("prodRegion")}:${this.account}:stateMachine:*`, + ], + }), + ], + }), + ], + } + ); + + + // Lambda function to clean up CloudFormation stacks upon pipeline deletion, ensuring resources are properly removed. + const cleanUpFunction = new NodejsFunction(this, 'CleanUpFunction', { + runtime: Runtime.NODEJS_18_X, + entry: join(__dirname, '..', '..', 'src', 'services', 'delete-stacks.ts'), + handler: 'handler', + timeout: Duration.minutes(15), + }); + + // Grant permissions to list and delete CloudFormation stacks + cleanUpFunction.addToRolePolicy( + new PolicyStatement({ + actions: ['cloudformation:ListStacks', 'cloudformation:DeleteStack'], + resources: ['*'], + }) + ); + + // Create a custom resource to trigger the cleanup Lambda function on stack deletion + // When the CodePipelineStack is deleted using cdk destroy, the CleanupTrigger resource is triggered, which then invokes the cleanUpFunction asynchronously. + new AwsCustomResource(this, 'CleanupTrigger', { + onDelete: { + service: 'Lambda', + action: 'invoke', + parameters: { + FunctionName: cleanUpFunction.functionName, + InvocationType: 'Event', + }, + physicalResourceId: PhysicalResourceId.of('CleanupTrigger'), + }, + policy: AwsCustomResourcePolicy.fromStatements([ + new PolicyStatement({ + actions: ['lambda:InvokeFunction'], + resources: [cleanUpFunction.functionArn], + }), + ]), + }); + + + } +} + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/DataIngestionStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/DataIngestionStack.ts new file mode 100644 index 00000000..dad54ae4 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/DataIngestionStack.ts @@ -0,0 +1,548 @@ + +import { Construct } from 'constructs'; +import { Duration, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { BlockPublicAccess, Bucket, EventType, IBucket } from 'aws-cdk-lib/aws-s3'; +import { StringParameter } from 'aws-cdk-lib/aws-ssm'; +import { AttributeType, GlobalSecondaryIndexProps, GlobalSecondaryIndexPropsV2, TableV2 } from 'aws-cdk-lib/aws-dynamodb'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { join } from 'path'; +import { LambdaDestination } from 'aws-cdk-lib/aws-s3-notifications'; +import { Rule } from 'aws-cdk-lib/aws-events'; +import { LambdaInvoke, SqsSendMessage } from 'aws-cdk-lib/aws-stepfunctions-tasks'; +import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { Choice, Condition, DefinitionBody, Fail, JsonPath, Pass, Result, StateMachine, TaskInput, Wait, WaitTime } from 'aws-cdk-lib/aws-stepfunctions'; +import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { SfnStateMachine } from 'aws-cdk-lib/aws-events-targets'; +import { CfnSchedule } from "aws-cdk-lib/aws-scheduler"; +import { PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs'; +import { LogLevel } from 'aws-cdk-lib/aws-stepfunctions'; + + +export interface DataIngestionStackProps extends StackProps { + stageName: string; + codePipelineName: string; + knowledgeBaseId: string; + dataSourceId: string; +} + +// Define the enum for ingestion job statuses +enum IngestionJobStatus { + IN_PROGRESS = 'IN_PROGRESS', + STARTING = 'STARTING', + COMPLETE = 'COMPLETE', + FAILED = 'FAILED' +} + + +/** + * ## DataIngestionStack Overview + * + * ### Usage: + * This stack manages the ingestion of raw data, processing and tracks metadata in DynamoDB. + * It uses Step Functions to automate data ingestion workflows and trigger knowledge base updates. + * + * ### Key Features: + * - S3 Buckets: Stores raw and processed data thats used for Knowledge Base ingestion. + * - DynamoDB Table: Tracks file metadata and ingestion statuses. + * - Step Functions Workflow: Automates ETL (Extract, Transform, Load) processes. + * - Lambda Functions: Handles file upload events and metadata tracking. + * - EventBridge Integration: Captures S3 events and triggers workflows. + */ + + + +export class DataIngestionStack extends Stack { + public readonly kbDataIngestionStateMachineArn: string; + public readonly rawS3DataSourceBucketName: string; + + constructor(scope: Construct, id: string, props: DataIngestionStackProps) { + super(scope, id, props); + + + // ================================Create S3 Buckets================================ + // Create a s3 bucket that will be used as the organization's data source + const rawS3DataSource = this.createBucket('RawDataSourceBucket', 'raw-data-source-bucket', props.stageName); + + // Store the bucket name in SSM Parameter Store + new StringParameter(this, 'rawS3DataSource', { + parameterName: `/${props.codePipelineName}/${props.stageName}/raw-s3-data-source`, + stringValue: rawS3DataSource.bucketName, + }); + + this.rawS3DataSourceBucketName = rawS3DataSource.bucketName; + + // Create a s3 bucket that will be used as the organization's processed data source (KB s3 source) + const processedS3DataSource = this.createBucket('ProcessedDataSourceBucket', 'processed-data-source-bucket', props.stageName); + + // Store the bucket name in SSM Parameter Store + new StringParameter(this, 'processedS3DataSource', { + parameterName: `/${props.codePipelineName}/${props.stageName}/processed-s3-data-source`, + stringValue: processedS3DataSource.bucketName, + }); + + + // ================================Create DynamoDB Table================================ + + // Create a DynamoDB table - a unfied table to track both raw and processed files metadata + // Since there is a need to query the table based on the fileType and kbIngestionStatus to decide to start the KB ingestion process, + // a GSI is created on the fileType and kbIngestionStatus attributes; a query on GSI is faster than a scan on the entire table + const fileMetadataTable = new TableV2(this, 'FileMetadataTable', { + partitionKey: { name: 'fileId', type: AttributeType.STRING }, // Unique file identifier across raw/processed files + sortKey: { name: 'fileType', type: AttributeType.STRING }, // Sort key to differentiate raw vs processed files + removalPolicy: RemovalPolicy.DESTROY, // Change to RETAIN for production + tableName: `file-metadata-for-${props.codePipelineName}-${props.stageName}`, + globalSecondaryIndexes: [ + { + indexName: 'fileType-kbIngestionStatus-index', + partitionKey: { name: 'fileType', type: AttributeType.STRING }, + sortKey: { name: 'kbIngestionStatus', type: AttributeType.STRING }, + projectionType: 'ALL', // Include all attributes in the index + } as GlobalSecondaryIndexPropsV2, + ], + }); + + // Store the table name in SSM Parameter Store + new StringParameter(this, 'FileMetadataTableNameParameter', { + parameterName: `/${props.codePipelineName}/${props.stageName}/fileMetadataTableName`, + stringValue: fileMetadataTable.tableName, + }); + + + // Lambda function to mark the new files in the DynamoDB table + const metadataTrackingLambda = this.createLambdaFunction('MetadataTrackingLambda', 'metadata-tracking.ts', 5, { + FILE_METADATA_TABLE_NAME: fileMetadataTable.tableName, + }); + + // Grant permissions to the Lambda function for the unified metadata table and S3 buckets + fileMetadataTable.grantReadWriteData(metadataTrackingLambda); // Allow Lambda to read/write to the table + rawS3DataSource.grantRead(metadataTrackingLambda); // Read access for raw bucket + processedS3DataSource.grantRead(metadataTrackingLambda); // Read access for processed bucket + + // Trigger the Lambda function on object creation and deletion in the raw bucket + rawS3DataSource.addEventNotification( + EventType.OBJECT_CREATED, + new LambdaDestination(metadataTrackingLambda), + ); + rawS3DataSource.addEventNotification( + EventType.OBJECT_REMOVED, + new LambdaDestination(metadataTrackingLambda), + ); + + // Trigger the Lambda function on object creation and deletion in the processed bucket + processedS3DataSource.addEventNotification( + EventType.OBJECT_CREATED, //Use the s3:ObjectCreated:* event type to request notification regardless of the API that was used to create an object. + new LambdaDestination(metadataTrackingLambda), + ); + + processedS3DataSource.addEventNotification( + EventType.OBJECT_REMOVED, + new LambdaDestination(metadataTrackingLambda), + ); + + + // ===============================EventBridge Rule================================ + // Create an EventBridge rule to capture specific S3 events - Object Created and Object Deleted + // The rule is configured to capture events from the specified bucket and send it to Step Functions as a target + const s3EventRule = new Rule(this, 'S3EventRule', { + eventPattern: { + source: ['aws.s3'], + detailType: ['Object Created', 'Object Deleted'], + detail: { + bucket: { + name: [rawS3DataSource.bucketName], + } + }, + }, + }); + + + // ===============================ETL Step Function Workflow================================ + + // Create a Lambda function to process s3 events and extract the file name and event type + const s3EventProcessor = this.createLambdaFunction('S3EventProcessorLambda', 's3-event-processor.ts', 1); + + // Step 1: Create a Step Functions task to invoke the Lambda function + const lambdaInvokeTask = new LambdaInvoke(this, 'S3EventProcessorLambdaInvokeTask', { + lambdaFunction: s3EventProcessor, + outputPath: '$.Payload', // The outputPath property is set to $.Payload, which means that the output of the Lambda function will be extracted from the Payload field of the Lambda's response. + }); + + // Create SQS queue to send file upload events + const fileUploadQueue = new Queue(this, 'FileUploadQueue', { + queueName: 'file-upload-queue-org-data-source', + visibilityTimeout: Duration.seconds(300), + }); + + // Create another SQS queue to send file deletion events + const fileDeletionQueue = new Queue(this, 'FileDeletionQueue', { + queueName: 'file-deletion-queue-org-data-source', + visibilityTimeout: Duration.seconds(300), + }); + + // Step 3: Create a Step Functions task to add file upload message to the queue + const addFileUploadMessageToQueueTask = new SqsSendMessage(this, 'AddFileUploadMessageToQueueTask', { + queue: fileUploadQueue, + messageBody: TaskInput.fromObject({ + fileName: JsonPath.stringAt('$.fileName'), + eventType: JsonPath.stringAt('$.eventType'), + }), + }); + + // Step 4: Create a Step Functions task to add file deletion message to the queue + const addFileDeletionMessageToQueueTask = new SqsSendMessage(this, 'AddFileDeletionMessageToQueueTask', { + queue: fileDeletionQueue, + messageBody: TaskInput.fromObject({ + fileName: JsonPath.stringAt('$.fileName'), + eventType: JsonPath.stringAt('$.eventType'), + }), + }); + + // Create a Lambda function to process the added file by picking up the message from the FileUploadQueue + const fileProcessor = this.createLambdaFunction('FileProcessorLambda', 'file-upload-processor.ts', 5, { + RAW_S3: rawS3DataSource.bucketArn, + PROCESSED_S3: processedS3DataSource.bucketArn, + }); + + + // Add permissions to the Lambda function to access the S3 buckets + rawS3DataSource.grantRead(fileProcessor); + processedS3DataSource.grantWrite(fileProcessor); + + // Add the file upload SQS queue as an event source for the Lambda function + fileProcessor.addEventSource(new SqsEventSource(fileUploadQueue, { + batchSize: 1, + })); + + // Also add the file deletion SQS queue as an event source for the Lambda function + // This is to handle the case where the file is deleted from the raw S3 bucket, and we need to delete the corresponding file from the processed S3 bucket + // Same Lambda function can be used to handle both file upload and file deletion events + fileProcessor.addEventSource(new SqsEventSource(fileDeletionQueue, { + batchSize: 1, + })); + + + // Step 5: Create a Step Functions task to invoke the Lambda function to process the added file by picking up the message from the FileUploadQueue + const fileProcessorTask = new LambdaInvoke(this, 'FileProcessorLambdaInvokeTask', { + lambdaFunction: fileProcessor, + outputPath: '$.Payload', + }); + + // Step 2: Create a Choice state to determine the s3 event type based on the Lambda's response + const choiceState = new Choice(this, 'S3EventType'); + + // Define the states to transition to based on the eventType + const objectCreatedState = new Pass(this, 'ObjectCreated'); // Placeholder for actual state + const objectDeletedState = new Pass(this, 'ObjectDeleted'); // Placeholder for actual state + const defaultState = new Fail(this, 'UnknownEventType', { + error: 'UnknownEventType', + cause: 'The event type is not recognized.', + }); + + // Add conditions to the Choice state + choiceState + // .when(Condition.stringEquals('$.lambdaResult.eventType', 'Object Created'), objectCreatedState) + .when(Condition.stringEquals('$.eventType', 'Object Created'), objectCreatedState.next(addFileUploadMessageToQueueTask).next(fileProcessorTask)) + .when(Condition.stringEquals('$.eventType', 'Object Deleted'), objectDeletedState.next(addFileDeletionMessageToQueueTask).next(fileProcessorTask)) + .otherwise(defaultState); + + + // Define the state machine + const etlStateMachine = new StateMachine(this, 'ETLStateMachine', { + definition: lambdaInvokeTask.next(choiceState), + timeout: Duration.minutes(5), + }); + + // Add the state machine as a target to the EventBridge rule + s3EventRule.addTarget(new SfnStateMachine(etlStateMachine)); + + + // ===============================Bedrock Knowledge Base Ingestion Workflow================================ + + // =======================Step 1: Check for new files in the processed S3 bucket======================= + // Create a Lambda function to check for new files in the processed S3 bucket. + const checkForNewFileModificationsLambda = this.createLambdaFunction('CheckForNewFileModificationsLambda', 'check-for-new-files.ts', 5, { + FILE_METADATA_TABLE: fileMetadataTable.tableName, + }); + + // Grant read permissions to the DynamoDB table for the CheckForNewFiles Lambda. + fileMetadataTable.grantReadData(checkForNewFileModificationsLambda); + + // Create a Step Functions task to invoke the CheckForNewFiles Lambda. + const checkForNewFileModificationsTask = new LambdaInvoke(this, 'checkForNewFileModificationsTask', { + lambdaFunction: checkForNewFileModificationsLambda, + outputPath: '$.Payload', // Extract Lambda response payload. + }); + + + + + + + // =======================Step 2: Evaluate the result from CheckForNewFilesTask======================= + // Define a Choice state to evaluate the result from CheckForNewFiles. + const choiceStateTocheckForNewFileModifications = new Choice(this, 'CheckForNewFileModifications'); + + // Define the condition when new files are available. + // const newFilesAvailableCondition = Condition.booleanEquals('$.newFilesAvailable', true); + const filesModifiedCondition = Condition.booleanEquals('$.fileModificationsExist', true); + + + + + + + // =======================Step 3: Start the KB ingestion process if new files are available======================= + + // Create a Lambda function to start the KB ingestion process + const startKBIngestionLambda = this.createLambdaFunction('StartKBIngestionLambda', 'kb-data-ingestion.ts', 10, { + FILE_METADATA_TABLE: fileMetadataTable.tableArn, + PROCESSED_S3: processedS3DataSource.bucketArn, + KNOWLEDGE_BASE_ID: props.knowledgeBaseId, + DATA_SOURCE_ID: props.dataSourceId, + }); + + + + // Grant permissions to the Lambda function to access the DynamoDB table and start the KB ingestion + fileMetadataTable.grantReadData(startKBIngestionLambda); + // processedS3DataSource.grantRead(startKBIngestionLambda); + + // Allow the Lambda function to start the KB ingestion process + const allowKnowledgeBaseDataIngestion = new PolicyStatement({ + actions: ['bedrock:StartIngestionJob'], + resources: [`arn:aws:bedrock:${this.region}:${this.account}:knowledge-base/${props.knowledgeBaseId}`], + sid: 'AllowKnowledgeBaseDataIngestion', + }); + startKBIngestionLambda.addToRolePolicy(allowKnowledgeBaseDataIngestion); + + // Create a Step Functions task to invoke the Lambda function to start the KB ingestion + const startKBIngestionTask = new LambdaInvoke(this, 'StartKBIngestionLambdaInvokeTask', { + lambdaFunction: startKBIngestionLambda, + outputPath: '$.Payload', + }); + + + // =======================Step 4: Get the KB ingestion job status======================= + + // Create a Lambda function to get the KB ingestion job status + const getIngestionJobStatusLambda = this.createLambdaFunction('GetIngestionJobStatusLambda', 'get-ingestion-job-status.ts', 5, { + FILE_METADATA_TABLE: fileMetadataTable.tableArn, + }); + + // Allow the Lambda function to get the ingestion job status + const allowGetIngestionJobStatus = new PolicyStatement({ + actions: ['bedrock:GetIngestionJob'], + resources: [`arn:aws:bedrock:${this.region}:${this.account}:knowledge-base/${props.knowledgeBaseId}`], + sid: 'AllowGetIngestionJobStatus', + }); + getIngestionJobStatusLambda.addToRolePolicy(allowGetIngestionJobStatus); + + // Create a Step Functions task to invoke the Lambda function to get the ingestion job status + // The payload property is set to a JSON object that contains the KnowledgeBaseId, DataSourceId, and IngestionJobId + // payload property specifies the input to the Lambda function + // The resultPath property is set to $.GetIngestionJobResult, which means that the output of the Lambda function will be extracted from the GetIngestionJobResult field of the Lambda's response. + // The result of the Lambda function will be stored in the $.GetIngestionJobResult field of the state's output + const getIngestionJobStatusTask = new LambdaInvoke(this, 'GetIngestionJobStatusLambdaInvokeTask', { + lambdaFunction: getIngestionJobStatusLambda, + payload: TaskInput.fromObject({ + KnowledgeBaseId: JsonPath.stringAt("$.KnowledgeBaseId"), + DataSourceId: JsonPath.stringAt("$.DataSourceId"), + IngestionJobId: JsonPath.stringAt("$.JobId"), + }), + resultPath: "$.GetIngestionJobResult", + }); + + // =======================Step 4.1: Wait for the KB ingestion job to complete======================= + // Wait state to pause before polling the job status again + const waitState = new Wait(this, "Wait", { + time: WaitTime.duration(Duration.seconds(10)), // Adjust the wait time as needed + }); + + + // =======================Step 6: Update the file metadata table with the KB ingestion status======================= + // Create a Lambda function to update the file metadata table with the KB ingestion status + const updateFileMetadataLambda = this.createLambdaFunction('UpdateFileMetadataLambda', 'update-file-metadata.ts', 5, { + FILE_METADATA_TABLE: fileMetadataTable.tableArn, + }); + + + + // Grant the Lambda function permissions to read and write to the DynamoDB table + fileMetadataTable.grantReadWriteData(updateFileMetadataLambda); + + + // Create a Step Functions task to invoke the Lambda function to update the file metadata table with the KB ingestion status + const updateFileMetadataTask = new LambdaInvoke(this, 'UpdateFileMetadataLambdaInvokeTask', { + lambdaFunction: updateFileMetadataLambda, + outputPath: '$.Payload', + // outputPath: '$.Payload', // The outputPath property is set to $.Payload, which means that the output of the Lambda function will be extracted from the Payload field of the Lambda's response. + }); + + + + + + // =======================Step 5: Check the status of the ingestion job======================= + // Create a choice state to check the status of the ingestion job + const choiceStateForJobStatus = new Choice(this, 'CheckIngestionJobStatus'); + + + // Define the states to transition to based on the job status + const jobInProgressState = Condition.or( + Condition.stringEquals('$.GetIngestionJobResult.Payload.status', IngestionJobStatus.IN_PROGRESS), + Condition.stringEquals('$.GetIngestionJobResult.Payload.status', IngestionJobStatus.STARTING), + ); + const jobCompletedState = Condition.stringEquals('$.GetIngestionJobResult.Payload.status', IngestionJobStatus.COMPLETE); + + const jobFailedState = Condition.stringEquals('$.GetIngestionJobResult.Payload.status', IngestionJobStatus.FAILED); + + const jobCompletedPassState = new Pass(this, `IngestionJobCompleted-${props.stageName}`, { + result: Result.fromObject({ + message: `The ingestion job completed successfully in -${props.stageName} stage.`, + }), + }); + + + + // Pass states to indicate that the ingestion job failed + const jobFailedPassState = new Pass(this, 'IngestionJobFailed', { + result: Result.fromObject({ + message: 'The ingestion job failed.', + }), + }); + + // Fail state to indicate that the job status is not recognized + const unknownJobStatus = new Fail(this, 'UnknownJobStatus', { + error: 'UnknownJobStatus', + cause: 'The job status is not recognized.', + }); + + + + + + if (props.stageName === 'QA') { + // Lambda function to trigger the ragEvaluationStateMachine using the AWS SDK's StartExecution API, + const triggerRAGEvalLambda = this.createLambdaFunction('TriggerRAGEvaluationLambda', 'trigger-rag-evaluation.ts', 5, { + CODE_PIPELINE_NAME: props.codePipelineName, + }); + + + // Add permissions for the Lambda to start executions on Step Functions + triggerRAGEvalLambda.addToRolePolicy(new PolicyStatement({ + actions: ['states:StartExecution', 'ssm:GetParameter'], + resources: [`arn:aws:states:${this.region}:${this.account}:stateMachine:*`, // Allow starting execution of any state machine in this account + `arn:aws:ssm:${this.region}:${this.account}:parameter/${props.codePipelineName}/PostQAApproval/rag-evaluation-state-machine-arn`] // Allow fetching parameters for RAG evaluation + })); + + + // Create the Lambda invoke task to trigger the RAG evaluation state machine + const invokeTriggerRAGLambdaTask = new LambdaInvoke(this, 'InvokeTriggerRAGLambda', { + lambdaFunction: triggerRAGEvalLambda, + outputPath: '$.Payload', + }); + + + choiceStateForJobStatus + .when(jobInProgressState, waitState.next(getIngestionJobStatusTask)) + .when(jobCompletedState, jobCompletedPassState.next(updateFileMetadataTask.next(invokeTriggerRAGLambdaTask))) + .when(jobFailedState, jobFailedPassState) + .otherwise(unknownJobStatus); + + } else { + choiceStateForJobStatus + .when(jobInProgressState, waitState.next(getIngestionJobStatusTask)) + .when(jobCompletedState, jobCompletedPassState.next(updateFileMetadataTask)) + .when(jobFailedState, jobFailedPassState) + .otherwise(unknownJobStatus); + } + + + + // Configure the Choice state to transition to the appropriate state based on the new files availability + choiceStateTocheckForNewFileModifications + .when(filesModifiedCondition, startKBIngestionTask.next(getIngestionJobStatusTask).next(choiceStateForJobStatus)) + .otherwise(new Pass(this, 'No new file modifications to sync')); + + + + // Define the state machine for the KB data ingestion process + const stateMachineDefinition = checkForNewFileModificationsTask.next(choiceStateTocheckForNewFileModifications); + + const logGroup = new LogGroup(this, 'KBDataIngestionLogGroup', { + retention: RetentionDays.ONE_WEEK, + removalPolicy: RemovalPolicy.DESTROY, + }); + + // Create a Step Functions state machine to orchestrate the KB data ingestion process + const kbDataIngestionStateMachine = new StateMachine(this, 'KBDataIngestionStateMachine', { + definitionBody: DefinitionBody.fromChainable(stateMachineDefinition), + timeout: Duration.minutes(300), + logs: { + destination: logGroup, + includeExecutionData: true, + level: LogLevel.ALL, + }, + }); + + this.kbDataIngestionStateMachineArn = kbDataIngestionStateMachine.stateMachineArn; + + + + const kbIngestionSchedulerRole = new Role(this, 'KBIngestionSchedulerRole', { + assumedBy: new ServicePrincipal('scheduler.amazonaws.com'), + }); + + // Allow the scheduler to invoke the Step Functions state machine + kbDataIngestionStateMachine.grantStartExecution(kbIngestionSchedulerRole); + + + //Create an EventBridge scheduler to invoke the Step Functions state machine every 2 minutes + new CfnSchedule(this, "KBDataIngestionSchedule", { + flexibleTimeWindow: { + mode: "OFF", + }, + scheduleExpression: "cron(*/2 * * * ? *)", + target: { + arn: kbDataIngestionStateMachine.stateMachineArn, + roleArn: kbIngestionSchedulerRole.roleArn, + input: JSON.stringify({ + scheduleTime: "", + }), + }, + description: + "Schedule to start the Knowledge Bases data ingestion process", + name: "KnowledgeBasesDataIngestionSchedule", + state: "ENABLED", + }); + + + + + } + + private createBucket(id: string, namePrefix: string, stageName: string): Bucket { + return new Bucket(this, id, { + bucketName: `${namePrefix}-${process.env.CDK_DEFAULT_ACCOUNT}-${stageName.toLowerCase()}`, + blockPublicAccess: BlockPublicAccess.BLOCK_ALL, + autoDeleteObjects: true, + removalPolicy: RemovalPolicy.DESTROY, + eventBridgeEnabled: true, + }); + } + + private createLambdaFunction(id: string, entryPath: string, timeoutMinutes: number, environment: Record = {}): NodejsFunction { + return new NodejsFunction(this, id, { + runtime: Runtime.NODEJS_18_X, + entry: join(__dirname, '..', '..', 'src', 'services', entryPath), + handler: 'handler', + environment, + timeout: Duration.minutes(timeoutMinutes), + }); + + } + +} + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/MoveFilesStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/MoveFilesStack.ts new file mode 100644 index 00000000..ae8d7aca --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/MoveFilesStack.ts @@ -0,0 +1,127 @@ +// Import necessary AWS CDK modules and constructs. +import { CfnOutput, Duration, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib"; +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; +import { Construct } from "constructs"; +import { join } from "path"; +import { Runtime } from "aws-cdk-lib/aws-lambda"; +import { SSMParameterReader } from "./ssm-parameter-reader"; +import { PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { LambdaInvoke } from "aws-cdk-lib/aws-stepfunctions-tasks"; +import { DefinitionBody, StateMachine } from "aws-cdk-lib/aws-stepfunctions"; +import { StringParameter } from "aws-cdk-lib/aws-ssm"; +import { SSM } from "aws-sdk"; + + +export interface MoveFilesStackProps extends StackProps { + codePipelineName: string; + prodRawS3DataSourceBucketName: string; // Name of the Production raw S3 bucket. +} + +/** + * ## MoveFilesStack Overview + * + * ### Usage: + * This stack orchestrates the file transfer process between QA and Production environments. + * Once the RAG evaluation is complete, this stack copies raw files from the QA S3 bucket to the Production S3 bucket. + * + * ### Key Features: + * - Lambda function: Copies raw files from the QA raw S3 bucket to the Production raw S3 bucket. + * - Step Function State Machine: Automates the copying process. + * - SSM Parameter Store Integration: Stores and retrieves bucket names and the ARN of the state machine. + * - IAM Policies: Ensures the Lambda function has the required permissions to interact with S3. + * + * + */ + +export class MoveFilesStack extends Stack { + + constructor(scope: Construct, id: string, props: MoveFilesStackProps) { + super(scope, id, props); + + // Retrieve QA Bucket Name from SSM Parameter Store. + const qaRawS3DataSourceBucketParameter = new SSMParameterReader(this, 'QARawS3DataSourceBucketParameter', { + parameterName: `/${props.codePipelineName}/QA/raw-s3-data-source`, + region: this.node.tryGetContext("defaultRegion"), // SSM parameter region. + }); + + // Read the DynamoDB table name from SSM Parameter Store + // const fileMetadataTableNameParam = StringParameter.fromStringParameterName(this, + // 'FileMetadataTableName', `/${props.codePipelineName}/QA/fileMetadataTableName` + // ); + const fileMetadataTableNameParam = new SSMParameterReader(this, 'FileMetadataTableName', { + parameterName: `/${props.codePipelineName}/QA/fileMetadataTableName`, + region: this.node.tryGetContext("defaultRegion") // SSM parameter region. + }); + + // Resolve QA and Production Bucket Names. + const qaRawS3DataSourceBucketName = qaRawS3DataSourceBucketParameter.getParameterValue(); + const prodRawS3DataSourceBucketName = props.prodRawS3DataSourceBucketName; + + // Create Lambda Function for Copying Files from QA to Production. + const copyfilesFromQAToProdLambda = new NodejsFunction(this, 'FileCopyLambda', { + runtime: Runtime.NODEJS_18_X, + entry: join(__dirname, '..', '..', 'src', 'services', 'copy-files.ts'), + handler: 'handler', + timeout: Duration.minutes(15), + environment: { + RAW_S3_QA: qaRawS3DataSourceBucketName, // QA raw bucket as environment variable. + RAW_S3_PROD: prodRawS3DataSourceBucketName, // Production raw bucket as environment variable. + FILE_METADATA_TABLE_NAME: fileMetadataTableNameParam.getParameterValue(), + }, + }); + + // Define S3 Bucket ARNs. + const qaRawS3DataSourceBucketArn = `arn:aws:s3:::${qaRawS3DataSourceBucketName}`; + const prodRawS3DataSourceBucketArn = `arn:aws:s3:::${prodRawS3DataSourceBucketName}`; + + // Grant Lambda Function Permissions to Access QA and Production Buckets. + copyfilesFromQAToProdLambda.addToRolePolicy( + new PolicyStatement({ + actions: ['s3:ListBucket', 's3:GetObject', 's3:PutObject'], // Required S3 actions. + resources: [ + qaRawS3DataSourceBucketArn, `${qaRawS3DataSourceBucketArn}/*`, + prodRawS3DataSourceBucketArn, `${prodRawS3DataSourceBucketArn}/*` + ], + }) + ); + + // Construct the ARN for the DynamoDB table + const fileMetadataTableArn = `arn:aws:dynamodb:${this.node.tryGetContext('defaultRegion')}:${Stack.of(this).account}:table/${fileMetadataTableNameParam.getParameterValue()}`; // DynamoDB table ARN which is in the QA region. + + // Grant Lambda Function Permissions to Access DynamoDB Table. + copyfilesFromQAToProdLambda.addToRolePolicy( + new PolicyStatement({ + actions: ['dynamodb:Scan', 'dynamodb:UpdateItem'], // Required DynamoDB actions. + resources: [fileMetadataTableArn], // DynamoDB table ARN. + }) + ); + + + // Define a Step Function Task to Invoke the Copy Files Lambda. + const copyFilesTask = new LambdaInvoke(this, 'CopyDataLambdaInvokeTask', { + lambdaFunction: copyfilesFromQAToProdLambda, + outputPath: '$.Payload', // Output Lambda response. + }); + + // Define the Step Function State Machine to Automate File Movement. + const moveFilesStateMachineDefinition = copyFilesTask; + + const moveFilesStateMachine = new StateMachine(this, 'MoveFilesStateMachine', { + definitionBody: DefinitionBody.fromChainable(moveFilesStateMachineDefinition), + timeout: Duration.minutes(20), // State machine timeout. + }); + + // Store State Machine ARN in SSM Parameter Store for Future Reference. + new StringParameter(this, 'MoveFilesStateMachineArnParameter', { + parameterName: `/${props.codePipelineName}/Prod/move-files-state-machine-arn`, + stringValue: moveFilesStateMachine.stateMachineArn, + }); + + // // Output State Machine ARN to CloudFormation for Debugging and Reference. + // new CfnOutput(this, 'MoveFilesStateMachineArnOutput', { + // value: moveFilesStateMachine.stateMachineArn, + // description: 'ARN of the Move Files State Machine', + // }); + } +} + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/RAGEvaluationStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/RAGEvaluationStack.ts new file mode 100644 index 00000000..98c40f54 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/RAGEvaluationStack.ts @@ -0,0 +1,146 @@ +import { Duration, Stack, StackProps } from 'aws-cdk-lib'; +import { StringParameter } from 'aws-cdk-lib/aws-ssm'; +import { LambdaInvoke } from 'aws-cdk-lib/aws-stepfunctions-tasks'; +import { Choice, Condition, DefinitionBody, JsonPath, Pass, StateMachine, TaskInput } from 'aws-cdk-lib/aws-stepfunctions'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Construct } from 'constructs'; +import { join } from 'path'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; + +export interface RAGEvaluationStackProps extends StackProps { + stageName: string; + codePipelineName: string; +} + +/** + * ## RAGEvaluationStack Overview + * + * ### Usage: + * This stack manages the evaluation of the RAG pipeline based on data or code changes. + * It ensures that updates are only promoted to production after successful evaluations. + * + * ### Key Features: + * - Lambda Functions: Handles data ingestion evaluations and triggers approvals. + * - Step Functions State Machine: Automates the evaluation and approval process. + * - Manual Approval Step: Ensures that only validated changes are promoted to production. + * - SSM Parameter Store Integration: Stores configuration values like state machine ARNs. + * - IAM Policies: Grants the necessary permissions to manage CodePipeline and Step Functions. + */ + + +export class RAGEvaluationStack extends Stack { + constructor(scope: Construct, id: string, props: RAGEvaluationStackProps) { + super(scope, id, props); + + // Read the DynamoDB table name from SSM Parameter Store + const fileMetadataTableNameParam = StringParameter.fromStringParameterName(this, + 'FileMetadataTableName', `/${props.codePipelineName}/QA/fileMetadataTableName` + ); + + // Lambda to evaluate new data ingestion + const ingestionEvaluationLambda = this.createLambdaFunction('RAGEvaluationLambda', 'evaluate-new-data-ingestion.ts', 5, { + STAGE_NAME: props.stageName, + FILE_METADATA_TABLE_NAME: fileMetadataTableNameParam.stringValue, + }); + + // Construct the ARN for the DynamoDB table + const fileMetadataTableArn = `arn:aws:dynamodb:${Stack.of(this).region}:${Stack.of(this).account}:table/${fileMetadataTableNameParam.stringValue}`; + + // Add necessary permissions for ingestionEvaluationLambda + const allowDynamoDBAccess = new PolicyStatement({ + actions: ['dynamodb:Scan', 'dynamodb:UpdateItem'], + resources: [fileMetadataTableArn], + }); + ingestionEvaluationLambda.addToRolePolicy(allowDynamoDBAccess); + + + const triggerApprovalLambda = this.createLambdaFunction('TriggerApprovalLambda', 'trigger-approval.ts', 5, { + STAGE_NAME: props.stageName, + PIPELINE_NAME: props.codePipelineName, + }); + + // Add necessary permissions for triggerApprovalLambda + triggerApprovalLambda.addToRolePolicy(new PolicyStatement({ + actions: ['codepipeline:GetPipelineState', 'codepipeline:PutApprovalResult', 'ssm:GetParameter'], + resources: [ + `arn:aws:codepipeline:${this.region}:${this.account}:${props.codePipelineName}`, + `arn:aws:ssm:${this.node.tryGetContext("prodRegion")}:${this.account}:parameter/${props.codePipelineName}/Prod/move-files-state-machine-arn`, + `arn:aws:codepipeline:${this.region}:${this.account}:${props.codePipelineName}/${props.stageName}/ManualApprovalForProduction`, // The manual approval action resource + ], + })); + + + // Step 1: Invoke RAG evaluation lambda + const evaluationLambdaInvokeTask = new LambdaInvoke(this, 'EvaluationLambdaInvokeTask', { + lambdaFunction: ingestionEvaluationLambda, + outputPath: '$.Payload', + }); + + // Step 2: Invoke QA to Prod Approval Lambda + const invokeQAToProdPipelineApprovalLambdaTask = new LambdaInvoke(this, 'InvokeQAToProdPipelineApprovalLambda', { + lambdaFunction: triggerApprovalLambda, + payload: TaskInput.fromObject({ + success: JsonPath.stringAt('$.success'), + message: JsonPath.stringAt('$.message'), + }), + outputPath: '$.Payload', + }); + + // StepFunctionsStartExecution construct in AWS CDK does not have a region property as part of its API. Instead, you need to manage cross-region invocations using AWS SDK calls within a Lambda function that runs in the region where the Step Function is located. + // Create a new Lambda to start the Step Function in ${this.node.tryGetContext("prodRegion")} + const startMoveFilesLambda = this.createLambdaFunction('StartMoveFilesLambda', 'start-move-files-state-machine.ts', 5, { + PIPELINE_NAME: props.codePipelineName, + }); + + // Add necessary permissions to start Step Function in ${this.node.tryGetContext("prodRegion")} + startMoveFilesLambda.addToRolePolicy(new PolicyStatement({ + actions: ['states:StartExecution', 'ssm:GetParameter'], + resources: [ + `arn:aws:states:${this.node.tryGetContext("prodRegion")}:${this.account}:stateMachine:*`, + `arn:aws:ssm:${this.node.tryGetContext("prodRegion")}:${this.account}:parameter/${props.codePipelineName}/Prod/move-files-state-machine-arn`, + ], + })); + + // Create the Lambda task to invoke the Step Function in ${this.node.tryGetContext("prodRegion")} + const invokeMoveFilesLambdaTask = new LambdaInvoke(this, 'InvokeMoveFilesLambdaTask', { + lambdaFunction: startMoveFilesLambda, + outputPath: '$.Payload', + }); + + // Step 4: Choice state to handle different outcomes from the approval lambda + const checkApprovalStatus = new Choice(this, 'CheckApprovalStatus') + .when(Condition.numberEquals('$.statusCode', 500), invokeMoveFilesLambdaTask) + .otherwise(new Pass(this, 'GoAheadAndEndTheStateMachine')); // Default success path + + // Define the state machine + const ragEvaluationStateMachineDefinition = evaluationLambdaInvokeTask + .next(invokeQAToProdPipelineApprovalLambdaTask) + .next(checkApprovalStatus); + + const ragEvaluationStateMachine = new StateMachine(this, 'RAGEvaluationStateMachine', { + definitionBody: DefinitionBody.fromChainable(ragEvaluationStateMachineDefinition), + timeout: Duration.minutes(20), + }); + + // Store the ARN in SSM + new StringParameter(this, 'RAGEvaluationStateMachineArnParameter', { + parameterName: `/${props.codePipelineName}/PostQAApproval/rag-evaluation-state-machine-arn`, + stringValue: ragEvaluationStateMachine.stateMachineArn, + }); + } + + + private createLambdaFunction(id: string, entryPath: string, timeoutMinutes: number, environment: Record = {}): NodejsFunction { + return new NodejsFunction(this, id, { + runtime: Runtime.NODEJS_18_X, + entry: join(__dirname, '..', '..', 'src', 'services', entryPath), + handler: 'handler', + environment, + timeout: Duration.minutes(timeoutMinutes), + }); + + } + + +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/S3BucketStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/S3BucketStack.ts new file mode 100644 index 00000000..20c6a778 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/S3BucketStack.ts @@ -0,0 +1,33 @@ +import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { Bucket, BlockPublicAccess } from 'aws-cdk-lib/aws-s3'; +import { StringParameter } from 'aws-cdk-lib/aws-ssm'; + +export interface S3BucketStackProps extends StackProps { + codePipelineName: string; + stageName: string; +} + +export class S3BucketStack extends Stack { + + constructor(scope: Construct, id: string, props: S3BucketStackProps) { + super(scope, id, props); + + // Create the S3 bucket for Lambda packages + const lambdaPackageBucket = new Bucket(this, 'LambdaPackageBucket', { + // bucketName: "cdk-multimodal-rag-bedrock-customlambda-package-bucket", + versioned: true, // Enable versioning + removalPolicy: RemovalPolicy.DESTROY, // Cleanup bucket in dev environments + blockPublicAccess: BlockPublicAccess.BLOCK_ALL, // Ensure no public access + autoDeleteObjects: true, // Automatically delete objects when the bucket is deleted + }); + + // Store the bucket name in SSM Parameter Store + new StringParameter(this, 'LambdaPackageBucketNameParameter', { + parameterName: `/${props.codePipelineName}/${props.stageName}/lambda-package-bucket-name`, + stringValue: lambdaPackageBucket.bucketName, + }); + + + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/WebApplicationStack.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/WebApplicationStack.ts new file mode 100644 index 00000000..5533fa74 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/WebApplicationStack.ts @@ -0,0 +1,196 @@ +import { CfnOutput, Duration, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as ecs from 'aws-cdk-lib/aws-ecs'; +import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { join } from 'path'; +import * as apigateway from 'aws-cdk-lib/aws-apigateway'; +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; + +export interface WebAppStackProps extends StackProps { + readonly knowledgeBaseId: string; + stageName?: string; + +} + +/** + * ## WebApplicationStack Overview + * + * ### Usage: + * This stack deploys the Streamlit web application, which serves as the interface for interacting with Amazon Bedrock Knowledge Base. + * It showcases a chat interface where users can interact with the RAG-powered system. + * + * ### Key Features: + * - Application Load Balancer: Provides public access to the Streamlit app. + * - API Gateway Integration: Exposes an API endpoint for invoking the backend Lambda function, which interacts with Bedrock. + * - Lambda Functions: Invokes Bedrock models to generate responses for the chat interface. + * - Fargate Service: Runs the containerized web application within an ECS cluster. + * - IAM Policies: Grants permissions for invoking Bedrock models and retrieving knowledge base data. + */ + + +export class WebApplicationStack extends Stack { + constructor(scope: Construct, id: string, props: WebAppStackProps) { + super(scope, id, props); + + // Create a new VPC with a specific CIDR range, 2 Availability Zones, and 1 NAT Gateway. + const vpc = new ec2.Vpc(this, 'MyVpc', { + maxAzs: 2, // Use two Availability Zones + ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), // CIDR block for the VPC + natGateways: 1, // Number of NAT Gateways + }); + + // Create a security group for the ECS service that allows all outbound traffic. + const ecsSecurityGroup = new ec2.SecurityGroup(this, 'EcsSecurityGroup', { + vpc, + allowAllOutbound: true, // Allow all outbound traffic + }); + + // Create a security group for the ALB that allows all outbound traffic. + const albSecurityGroup = new ec2.SecurityGroup(this, 'AlbSecurityGroup', { + vpc, + allowAllOutbound: true, // Allow all outbound traffic + }); + + // Allow incoming HTTP traffic (port 80) from the ALB security group to the ECS security group. + ecsSecurityGroup.addIngressRule(albSecurityGroup, ec2.Port.tcp(80), 'Allow traffic from ALB'); + + // Create an ECS cluster within the VPC, enabling Fargate capacity providers. + const ecsCluster = new ecs.Cluster(this, 'MyEcsCluster', { + vpc, + enableFargateCapacityProviders: true, // Enable Fargate capacity providers + }); + + // Define a Fargate task with specific CPU and memory limits. + const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'MyFargateTaskDefinition', { + memoryLimitMiB: 512, // Memory limit for the task + cpu: 256, // CPU units for the task + }); + + // Retrieve the modelId from cdk.json context + const modelId = this.node.tryGetContext('bedrockModelID'); + if (!modelId) { + throw new Error("modelId not found in cdk.json context."); + } + + // Create the Lambda function + const mainLambdaFunction = new NodejsFunction(this, 'MainLambdaFunction', { + runtime: Runtime.NODEJS_18_X, + entry: (join(__dirname, '..', '..', 'src', 'services', 'call-bedrock-lambda.ts')), + handler: 'handler', + timeout: Duration.minutes(5), + environment: { + STAGE_NAME: props?.stageName!, + KNOWLEDGE_BASE_ID: props.knowledgeBaseId, + MODEL_ID: modelId, + }, + }); + + // Grant the Lambda function permission to invoke Bedrock + mainLambdaFunction.addToRolePolicy( + new PolicyStatement({ + actions: ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream'], + resources: ['*'], // TODO: Restrict to specific models + }), + ); + + // Grant the Lambda function permission to access KnowledgeBase for Amazon Bedrock to retrieve and generate responses + const kbRetrieveAndGeneratePolicy = new PolicyStatement({ + actions: ['bedrock:RetrieveAndGenerate', 'bedrock:Retrieve'], + resources: [`arn:aws:bedrock:${this.region}:${process.env.CDK_DEFAULT_ACCOUNT}:knowledge-base/${props.knowledgeBaseId}`], + // arn:aws:bedrock:{Region}:{Account}:knowledge-base/{KnowledgeBaseId} + sid: 'KnowledgebaseRetrieveAndGeneratePolicy', + }); + + mainLambdaFunction.addToRolePolicy(kbRetrieveAndGeneratePolicy); + + // Define the API Gateway to trigger the Lambda function + const chatAPI = new apigateway.RestApi(this, 'InvokeBedrock', { + restApiName: 'InvokeBedrock API', + description: 'This service invokes the Bedrock Main Lambda function', + deployOptions: { + stageName: props?.stageName!, + loggingLevel: apigateway.MethodLoggingLevel.INFO, + dataTraceEnabled: true, + }, + }); + + // Create a new API Gateway resource with the path /invoke + const invokeResource = chatAPI.root.addResource('invoke'); + + // Define the Lambda integration for the API Gateway resource + const invokeIntegration = new apigateway.LambdaIntegration(mainLambdaFunction); + + // Add the Lambda integration to the API Gateway resource + invokeResource.addMethod('POST', invokeIntegration); + + // Output the API Gateway endpoint URL + new CfnOutput(this, 'ApiGatewayEndpoint', { + value: chatAPI.url, + }); + + // Build the container image from the local Dockerfile and push it to Amazon ECR. + // const containerImage = ecs.ContainerImage.fromAsset('./src/app'); + const containerImage = ecs.ContainerImage.fromAsset('./src'); + + // Add a container to the Fargate task definition, using the local container image. + const container = fargateTaskDefinition.addContainer('MyContainer', { + image: containerImage, // Container image + memoryLimitMiB: 512, // Memory limit for the container + cpu: 256, // CPU units for the container + logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'WebContainerLogs' }), // Enable logging + environment: { + STAGE_NAME: props?.stageName!, + APIGATEWAY_ENDPOINT: chatAPI.url, + }, + // entryPoint: ["app.handler"], + // command: ["streamlit", "run", "app.py"], + }); + + // Map port 8501 of the container to the host. + container.addPortMappings({ + containerPort: 8501, // Port on the container + protocol: ecs.Protocol.TCP, // TCP protocol + }); + + // Create a Fargate service for the task definition, placing tasks in private subnets. + const fargateService = new ecs.FargateService(this, 'MyFargateService', { + cluster: ecsCluster, // The ECS cluster where the service will run + taskDefinition: fargateTaskDefinition, // The task definition to use + securityGroups: [ecsSecurityGroup], // Security group for the service + vpcSubnets: { + subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, // Subnets for the service + }, + }); + + // Create an Application Load Balancer (ALB) that is internet-facing. + const alb = new elbv2.ApplicationLoadBalancer(this, 'MyAlb', { + vpc, + internetFacing: true, // ALB is internet-facing + securityGroup: albSecurityGroup, // Security group for the ALB + vpcSubnets: { + subnetType: ec2.SubnetType.PUBLIC, // Subnets for the ALB + }, + }); + + // Add a listener on the ALB that listens for HTTP (port 80) traffic. + const httpListener = alb.addListener('HttpListener', { + port: 80, // Listening on port 80 + open: true, // Allow anyone to connect + }); + + // Forward incoming requests to the Fargate service on port 80. + httpListener.addTargets('FargateServiceTarget', { + port: 8501, // Forward to the container's port 8501 + protocol: elbv2.ApplicationProtocol.HTTP, // HTTP protocol + targets: [fargateService], // Target the Fargate service + }); + + // Output the DNS name of the ALB. + new CfnOutput(this, 'AlbDnsName', { + value: alb.loadBalancerDnsName, // DNS name of the ALB + }); + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/ssm-parameter-reader.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/ssm-parameter-reader.ts new file mode 100644 index 00000000..603dd331 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stacks/ssm-parameter-reader.ts @@ -0,0 +1,48 @@ +import { Arn, Stack } from 'aws-cdk-lib'; +import * as CustomResource from 'aws-cdk-lib/custom-resources'; +import { Construct } from 'constructs'; + +interface SSMParameterReaderProps { + parameterName: string; + region: string; +} + +function removeLeadingSlash(value: string): string { + return value.slice(0, 1) == '/' ? value.slice(1) : value; +} +export class SSMParameterReader extends CustomResource.AwsCustomResource { + constructor(scope: Construct, name: string, props: SSMParameterReaderProps) { + const { parameterName, region } = props; + + const ssmAwsSdkCall: CustomResource.AwsSdkCall = { + service: 'SSM', + action: 'getParameter', + parameters: { + Name: parameterName, + }, + region, + physicalResourceId: CustomResource.PhysicalResourceId.of(Date.now().toString()), + + }; + + const ssmCrPolicy = CustomResource.AwsCustomResourcePolicy.fromSdkCalls({ + resources: [ + Arn.format( + { + service: 'ssm', + region: props.region, + resource: 'parameter', + resourceName: removeLeadingSlash(parameterName), + }, + Stack.of(scope), + ), + ], + }); + + super(scope, name, { onUpdate: ssmAwsSdkCall, policy: ssmCrPolicy }); + } + + public getParameterValue(): string { + return this.getResponseField('Parameter.Value').toString(); + } +} \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stages/CodePipelineStage.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stages/CodePipelineStage.ts new file mode 100644 index 00000000..e61ab793 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/stages/CodePipelineStage.ts @@ -0,0 +1,88 @@ +import { Stage, StageProps } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { WebApplicationStack } from "../stacks/WebApplicationStack"; +import { BedrockStack } from "../stacks/BedrockStack"; +import { RAGEvaluationStack } from "../stacks/RAGEvaluationStack"; +import { DataIngestionStack } from "../stacks/DataIngestionStack"; +import { MoveFilesStack } from "../stacks/MoveFilesStack"; +import { S3BucketStack } from "../stacks/S3BucketStack"; + + +export interface CodePipelineStageProps extends StageProps { + stageName: string; + codePipelineName: string; +} + +export class CodePipelineStage extends Stage { + + constructor(scope: Construct, id: string, props: CodePipelineStageProps) { + super(scope, id, props); + + if (props.stageName === "PreQABucketSetupStage" || props.stageName === "PreProdBucketSetupStage") { + + new S3BucketStack(this, "S3BucketStack", { + codePipelineName: props.codePipelineName as string, + stageName: props.stageName as string, + }); + } + + if (props.stageName === "QA") { + + const bedrockKnowledgeBaseStack = new BedrockStack(this, "BedrockStack", { + stageName: props.stageName, + codePipelineName: props.codePipelineName, + }); + + new DataIngestionStack(this, "DataIngestionStack", { + stageName: props.stageName as string, + codePipelineName: props.codePipelineName as string, + knowledgeBaseId: bedrockKnowledgeBaseStack.knowledgeBaseId, + dataSourceId: bedrockKnowledgeBaseStack.dataSourceId, + }); + + new WebApplicationStack(this, "WebAppStack", { + stageName: props.stageName, + knowledgeBaseId: bedrockKnowledgeBaseStack.knowledgeBaseId, + }); + + } + + // New PostQAApproval stage + if (props.stageName === "PostQAApproval") { + // Create only the RAG evaluation stack in this stage + new RAGEvaluationStack(this, "RAGEvaluationStack", { + stageName: props.stageName as string, + codePipelineName: props.codePipelineName as string, + }); + + // This stage will handle the Manual Approval and the actual RAG evaluation trigger + // LambdaInvoke for RAG evaluation would trigger here based on the KB ingestion step machine. + } + + if (props.stageName === "Prod") { + const bedrockKnowledgeBaseStack = new BedrockStack(this, "BedrockStack", { + stageName: props.stageName, + codePipelineName: props.codePipelineName, + }); + + const dataIngestionStack = new DataIngestionStack(this, "DataIngestionStack", { + stageName: props.stageName as string, + codePipelineName: props.codePipelineName as string, + knowledgeBaseId: bedrockKnowledgeBaseStack.knowledgeBaseId, + dataSourceId: bedrockKnowledgeBaseStack.dataSourceId, + }); + + new MoveFilesStack(this, "MoveFilesStack", { + codePipelineName: props.codePipelineName as string, + prodRawS3DataSourceBucketName: dataIngestionStack.rawS3DataSourceBucketName, + }); + + new WebApplicationStack(this, "WebAppStack", { + stageName: props.stageName, + knowledgeBaseId: bedrockKnowledgeBaseStack.knowledgeBaseId, + }); + + } + + } +} \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/utils/CustomBedrockKnowledgeBaseModels.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/utils/CustomBedrockKnowledgeBaseModels.ts new file mode 100644 index 00000000..2cce9ed6 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/lib/utils/CustomBedrockKnowledgeBaseModels.ts @@ -0,0 +1,9 @@ +import { BedrockKnowledgeBaseModels } from '@aws/agents-for-amazon-bedrock-blueprints'; + +export class CustomBedrockKnowledgeBaseModels extends BedrockKnowledgeBaseModels { + public static readonly TITAN_EMBED_TEXT_V2 = new CustomBedrockKnowledgeBaseModels("amazon.titan-embed-text-v2:0", 512); + + constructor(modelName: string, vectorDimension: number) { + super(modelName, vectorDimension); + } +} \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/package-lock.json b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/package-lock.json new file mode 100644 index 00000000..5800f3aa --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/package-lock.json @@ -0,0 +1,6307 @@ +{ + "name": "cdk-genai-app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cdk-genai-app", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-bedrock-agent-runtime": "^3.651.1", + "@aws-sdk/client-bedrock-runtime": "^3.645.0", + "@aws-sdk/client-codepipeline": "^3.650.0", + "@aws-sdk/client-lambda": "^3.651.1", + "@aws-sdk/client-s3": "^3.645.0", + "@aws/agents-for-amazon-bedrock-blueprints": "^1.0.0", + "@cdklabs/generative-ai-cdk-constructs": "^0.1.264", + "archiver": "^7.0.1", + "aws-lambda": "^1.0.7", + "aws-sdk": "^2.1691.0", + "esbuild": "^0.23.1", + "pdfreader": "^3.0.5" + }, + "devDependencies": { + "@types/archiver": "^6.0.2", + "@types/aws-lambda": "^8.10.145", + "@types/node": "^22.5.0", + "aws-cdk": "^2.154.1", + "aws-cdk-lib": "^2.154.1", + "constructs": "^10.3.0", + "ts-node": "^10.9.2", + "typescript": "^5.5.4" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.202", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", + "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/asset-kubectl-v20": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.3.tgz", + "integrity": "sha512-twhuEG+JPOYCYPx/xy5uH2+VUsIEhPTzDY0F1KuB+ocjWWB/KEDiOVL19nHvbPCB6fhWnkykXEMJ4HHcKvjtvg==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/cloud-assembly-schema": { + "version": "36.0.16", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.0.16.tgz", + "integrity": "sha512-CJjlb47SSbFKNQ10JYHWY+eYx4VoR/swu+/aG1rWoDsLMNoPIFc52PhckXcF3Xoo0JQWSUWYDJXihozpUqqcMw==", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "license": "Apache-2.0", + "dependencies": { + "jsonschema": "^1.4.1", + "semver": "^7.6.3" + }, + "engines": { + "node": ">= 18.18.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-lambda-powertools/commons": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-2.7.0.tgz", + "integrity": "sha512-IHDwmjJLiEVu8GfpHaHPrd7kEycHm/6Qh/6ssWGtyNZVDDJA/RzBmiRBnedx/As0h5njJmR28eNEkCNFA7rdSA==", + "license": "MIT-0" + }, + "node_modules/@aws-lambda-powertools/logger": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-2.7.0.tgz", + "integrity": "sha512-6nY26q7N5qH8eIDjV3ZBFxtNHx5M6cLQeQDP7kvdGPhRI3N0xWLsiaedMTByzRqaIDYjV3VXOaAIc9r59vCgbg==", + "license": "MIT-0", + "dependencies": { + "@aws-lambda-powertools/commons": "^2.7.0", + "lodash.merge": "^4.6.2" + }, + "peerDependencies": { + "@middy/core": "4.x || 5.x" + }, + "peerDependenciesMeta": { + "@middy/core": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-bedrock-agent": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-agent/-/client-bedrock-agent-3.645.0.tgz", + "integrity": "sha512-ys+2CtdB5jIcOaQpMzoolbu7UwbnTJss4exvT67yDT8nI9tIiMaqxaFDxT/konAwWZiGRzLilbyZXtnlCesiIQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-agent-runtime/-/client-bedrock-agent-runtime-3.651.1.tgz", + "integrity": "sha512-eSFFcOCsqBL7Dx8k3A2qtsW3mgeaIV9jfftoTkCg3KjOcMhvuya4PwE+i5O22+MbkCihXYuD/DVZ/fDAKeGxhA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.651.1", + "@aws-sdk/client-sts": "3.651.1", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/credential-provider-node": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/eventstream-serde-browser": "^3.0.7", + "@smithy/eventstream-serde-config-resolver": "^3.0.4", + "@smithy/eventstream-serde-node": "^3.0.6", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/client-sso": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.651.1.tgz", + "integrity": "sha512-Fm8PoMgiBKmmKrY6QQUGj/WW6eIiQqC1I0AiVXfO+Sqkmxcg3qex+CZBAYrTuIDnvnc/89f9N4mdL8V9DRn03Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.651.1.tgz", + "integrity": "sha512-PKwAyTJW8pgaPIXm708haIZWBAwNycs25yNcD7OQ3NLcmgGxvrx6bSlhPEGcvwdTYwQMJsdx8ls+khlYbLqTvQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/credential-provider-node": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.651.1" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/client-sts": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.651.1.tgz", + "integrity": "sha512-4X2RqLqeDuVLk+Omt4X+h+Fa978Wn+zek/AM4HSPi4C5XzRBEFLRRtOQUvkETvIjbEwTYQhm0LdgzcBH4bUqIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.651.1", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/credential-provider-node": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/core": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.651.1.tgz", + "integrity": "sha512-eqOq3W39K+5QTP5GAXtmP2s9B7hhM2pVz8OPe5tqob8o1xQgkwdgHerf3FoshO9bs0LDxassU/fUSz1wlwqfqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.1", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/property-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/signature-v4": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-middleware": "^3.0.4", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.649.0.tgz", + "integrity": "sha512-tViwzM1dauksA3fdRjsg0T8mcHklDa8EfveyiQKK6pUJopkqV6FQx+X5QNda0t/LrdEVlFZvwHNdXqOEfc83TA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.649.0.tgz", + "integrity": "sha512-ODAJ+AJJq6ozbns6ejGbicpsQ0dyMOpnGlg0J9J0jITQ05DKQZ581hdB8APDOZ9N8FstShP6dLZflSj8jb5fNA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-stream": "^3.1.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.651.1.tgz", + "integrity": "sha512-yOzPC3GbwLZ8IYzke4fy70ievmunnBUni/MOXFE8c9kAIV+/RMC7IWx14nAAZm0gAcY+UtCXvBVZprFqmctfzA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.649.0", + "@aws-sdk/credential-provider-http": "3.649.0", + "@aws-sdk/credential-provider-process": "3.649.0", + "@aws-sdk/credential-provider-sso": "3.651.1", + "@aws-sdk/credential-provider-web-identity": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.651.1" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.651.1.tgz", + "integrity": "sha512-QKA74Qs83FTUz3jS39kBuNbLAnm6cgDqomm7XS/BkYgtUq+1lI9WL97astNIuoYvumGIS58kuIa+I3ycOA4wgw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.649.0", + "@aws-sdk/credential-provider-http": "3.649.0", + "@aws-sdk/credential-provider-ini": "3.651.1", + "@aws-sdk/credential-provider-process": "3.649.0", + "@aws-sdk/credential-provider-sso": "3.651.1", + "@aws-sdk/credential-provider-web-identity": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.649.0.tgz", + "integrity": "sha512-6VYPQpEVpU+6DDS/gLoI40ppuNM5RPIEprK30qZZxnhTr5wyrGOeJ7J7wbbwPOZ5dKwta290BiJDU2ipV8Y9BQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.651.1.tgz", + "integrity": "sha512-7jeU+Jbn65aDaNjkjWDQcXwjNTzpYNKovkSSRmfVpP5WYiKerVS5mrfg3RiBeiArou5igCUtYcOKlRJiGRO47g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.651.1", + "@aws-sdk/token-providers": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.649.0.tgz", + "integrity": "sha512-XVk3WsDa0g3kQFPmnCH/LaCtGY/0R2NDv7gscYZSXiBZcG/fixasglTprgWSp8zcA0t7tEIGu9suyjz8ZwhymQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.649.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.649.0.tgz", + "integrity": "sha512-PjAe2FocbicHVgNNwdSZ05upxIO7AgTPFtQLpnIAmoyzMcgv/zNB5fBn3uAnQSAeEPPCD+4SYVEUD1hw1ZBvEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/middleware-logger": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.649.0.tgz", + "integrity": "sha512-qdqRx6q7lYC6KL/NT9x3ShTL0TBuxdkCczGzHzY3AnOoYUjnCDH7Vlq867O6MAvb4EnGNECFzIgtkZkQ4FhY5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.649.0.tgz", + "integrity": "sha512-IPnO4wlmaLRf6IYmJW2i8gJ2+UPXX0hDRv1it7Qf8DpBW+lGyF2rnoN7NrFX0WIxdGOlJF1RcOr/HjXb2QeXfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.649.0.tgz", + "integrity": "sha512-q6sO10dnCXoxe9thobMJxekhJumzd1j6dxcE1+qJdYKHJr6yYgWbogJqrLCpWd30w0lEvnuAHK8lN2kWLdJxJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.649.0.tgz", + "integrity": "sha512-xURBvdQXvRvca5Du8IlC5FyCj3pkw8Z75+373J3Wb+vyg8GjD14HfKk1Je1HCCQDyIE9VB/scYDcm9ri0ppePw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/token-providers": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.649.0.tgz", + "integrity": "sha512-ZBqr+JuXI9RiN+4DSZykMx5gxpL8Dr3exIfFhxMiwAP3DQojwl0ub8ONjMuAjq9OvmX6n+jHZL6fBnNgnNFC8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.649.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/types": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.649.0.tgz", + "integrity": "sha512-PuPw8RysbhJNlaD2d/PzOTf8sbf4Dsn2b7hwyGh7YVG3S75yTpxSAZxrnhKsz9fStgqFmnw/jUfV/G+uQAeTVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/util-endpoints": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.649.0.tgz", + "integrity": "sha512-bZI1Wc3R/KibdDVWFxX/N4AoJFG4VJ92Dp4WYmOrVD6VPkb8jPz7ZeiYc7YwPl8NoDjYyPneBV0lEoK/V8OKAA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "@smithy/util-endpoints": "^2.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.649.0.tgz", + "integrity": "sha512-IY43r256LhKAvdEVQO/FPdUyVpcZS5EVxh/WHVdNzuN1bNLoUK2rIzuZqVA0EGguvCxoXVmQv9m50GvG7cGktg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-bedrock-agent-runtime/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.649.0.tgz", + "integrity": "sha512-x5DiLpZDG/AJmCIBnE3Xhpwy35QIo3WqNiOpw6ExVs1NydbM/e90zFPSfhME0FM66D/WorigvluBxxwjxDm/GA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-bedrock-agent/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.645.0.tgz", + "integrity": "sha512-a5UY1DT1F8HyTU0uwO0rgyBGPkn74QdUWvSaFz6mSX0rfEIVATtreSOZBiAptrZDfh1wPDJjz0CBA9FZVL75ag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-codepipeline/-/client-codepipeline-3.650.0.tgz", + "integrity": "sha512-XGLUh4EQmLPVTiJmqBhJcQ2VQH5RQ6b0vl9ab8bU+9p7kFvtEnaxWX/nN8wFCesbFm5II+FoCaOhozDujrfE3A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.650.0", + "@aws-sdk/client-sts": "3.650.0", + "@aws-sdk/core": "3.649.0", + "@aws-sdk/credential-provider-node": "3.650.0", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/client-sso": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.650.0.tgz", + "integrity": "sha512-YKm14gCMChD/jlCisFlsVqB8HJujR41bl4Fup2crHwNJxhD/9LTnzwMiVVlBqlXr41Sfa6fSxczX2AMP8NM14A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.649.0", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.650.0.tgz", + "integrity": "sha512-6J7IS0f8ovhvbIAZaynOYP+jPX8344UlTjwHxjaXHgFvI8axu3+NslKtEEV5oHLhgzDvrKbinsu5lgE2n4Sqng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.649.0", + "@aws-sdk/credential-provider-node": "3.650.0", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.650.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/client-sts": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.650.0.tgz", + "integrity": "sha512-ISK0ZQYA7O5/WYgslpWy956lUBudGC9d7eL0FFbiL0j50N80Gx3RUv22ezvZgxJWE0W3DqNr4CE19sPYn4Lw8g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.650.0", + "@aws-sdk/core": "3.649.0", + "@aws-sdk/credential-provider-node": "3.650.0", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/core": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.649.0.tgz", + "integrity": "sha512-dheG/X2y25RHE7K+TlS32kcy7TgDg1OpWV44BQRoE0OBPAWmFR1D1qjjTZ7WWrdqRPKzcnDj1qED8ncyncOX8g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.1", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/property-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/signature-v4": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-middleware": "^3.0.4", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.649.0.tgz", + "integrity": "sha512-tViwzM1dauksA3fdRjsg0T8mcHklDa8EfveyiQKK6pUJopkqV6FQx+X5QNda0t/LrdEVlFZvwHNdXqOEfc83TA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.649.0.tgz", + "integrity": "sha512-ODAJ+AJJq6ozbns6ejGbicpsQ0dyMOpnGlg0J9J0jITQ05DKQZ581hdB8APDOZ9N8FstShP6dLZflSj8jb5fNA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-stream": "^3.1.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.650.0.tgz", + "integrity": "sha512-x2M9buZxIsKuUbuDgkGHhAKYBpn0/rYdKlwuFuOhXyyAcnhvPj0lgNF2KE4ld/GF1mKr7FF/uV3G9lM6PFaYmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.649.0", + "@aws-sdk/credential-provider-http": "3.649.0", + "@aws-sdk/credential-provider-process": "3.649.0", + "@aws-sdk/credential-provider-sso": "3.650.0", + "@aws-sdk/credential-provider-web-identity": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.650.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.650.0.tgz", + "integrity": "sha512-uBra5YjzS/gWSekAogfqJfY6c+oKQkkou7Cjc4d/cpMNvQtF1IBdekJ7NaE1RfsDEz3uH1+Myd07YWZAJo/2Qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.649.0", + "@aws-sdk/credential-provider-http": "3.649.0", + "@aws-sdk/credential-provider-ini": "3.650.0", + "@aws-sdk/credential-provider-process": "3.649.0", + "@aws-sdk/credential-provider-sso": "3.650.0", + "@aws-sdk/credential-provider-web-identity": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.649.0.tgz", + "integrity": "sha512-6VYPQpEVpU+6DDS/gLoI40ppuNM5RPIEprK30qZZxnhTr5wyrGOeJ7J7wbbwPOZ5dKwta290BiJDU2ipV8Y9BQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.650.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.650.0.tgz", + "integrity": "sha512-069nkhcwximbvyGiAC6Fr2G+yrG/p1S3NQ5BZ2cMzB1hgUKo6TvgFK7nriYI4ljMQ+UWxqPwIdTqiUmn2iJmhg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.650.0", + "@aws-sdk/token-providers": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.649.0.tgz", + "integrity": "sha512-XVk3WsDa0g3kQFPmnCH/LaCtGY/0R2NDv7gscYZSXiBZcG/fixasglTprgWSp8zcA0t7tEIGu9suyjz8ZwhymQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.649.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.649.0.tgz", + "integrity": "sha512-PjAe2FocbicHVgNNwdSZ05upxIO7AgTPFtQLpnIAmoyzMcgv/zNB5fBn3uAnQSAeEPPCD+4SYVEUD1hw1ZBvEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/middleware-logger": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.649.0.tgz", + "integrity": "sha512-qdqRx6q7lYC6KL/NT9x3ShTL0TBuxdkCczGzHzY3AnOoYUjnCDH7Vlq867O6MAvb4EnGNECFzIgtkZkQ4FhY5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.649.0.tgz", + "integrity": "sha512-IPnO4wlmaLRf6IYmJW2i8gJ2+UPXX0hDRv1it7Qf8DpBW+lGyF2rnoN7NrFX0WIxdGOlJF1RcOr/HjXb2QeXfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.649.0.tgz", + "integrity": "sha512-q6sO10dnCXoxe9thobMJxekhJumzd1j6dxcE1+qJdYKHJr6yYgWbogJqrLCpWd30w0lEvnuAHK8lN2kWLdJxJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.649.0.tgz", + "integrity": "sha512-xURBvdQXvRvca5Du8IlC5FyCj3pkw8Z75+373J3Wb+vyg8GjD14HfKk1Je1HCCQDyIE9VB/scYDcm9ri0ppePw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/token-providers": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.649.0.tgz", + "integrity": "sha512-ZBqr+JuXI9RiN+4DSZykMx5gxpL8Dr3exIfFhxMiwAP3DQojwl0ub8ONjMuAjq9OvmX6n+jHZL6fBnNgnNFC8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.649.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/types": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.649.0.tgz", + "integrity": "sha512-PuPw8RysbhJNlaD2d/PzOTf8sbf4Dsn2b7hwyGh7YVG3S75yTpxSAZxrnhKsz9fStgqFmnw/jUfV/G+uQAeTVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/util-endpoints": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.649.0.tgz", + "integrity": "sha512-bZI1Wc3R/KibdDVWFxX/N4AoJFG4VJ92Dp4WYmOrVD6VPkb8jPz7ZeiYc7YwPl8NoDjYyPneBV0lEoK/V8OKAA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "@smithy/util-endpoints": "^2.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.649.0.tgz", + "integrity": "sha512-IY43r256LhKAvdEVQO/FPdUyVpcZS5EVxh/WHVdNzuN1bNLoUK2rIzuZqVA0EGguvCxoXVmQv9m50GvG7cGktg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.649.0.tgz", + "integrity": "sha512-x5DiLpZDG/AJmCIBnE3Xhpwy35QIo3WqNiOpw6ExVs1NydbM/e90zFPSfhME0FM66D/WorigvluBxxwjxDm/GA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-codepipeline/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.645.0.tgz", + "integrity": "sha512-nBfWDzWBQI1NCHYqBAmiifhdnLRxQYozaq6OjTuRcALjYJbOdFV7t0w9FWGISOq1OnM7r8UdCXlr2bzdyU0tJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.651.1.tgz", + "integrity": "sha512-/8+j1m5+hJ1fUZLWr+bC/R1/ScGIasR1Kj0jCwJUXZL+ZjKaggiy7sVmVC7DZdaD/hKCdts8SpUeXZejPjSiFg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.651.1", + "@aws-sdk/client-sts": "3.651.1", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/credential-provider-node": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/eventstream-serde-browser": "^3.0.7", + "@smithy/eventstream-serde-config-resolver": "^3.0.4", + "@smithy/eventstream-serde-node": "^3.0.6", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-stream": "^3.1.4", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.651.1.tgz", + "integrity": "sha512-Fm8PoMgiBKmmKrY6QQUGj/WW6eIiQqC1I0AiVXfO+Sqkmxcg3qex+CZBAYrTuIDnvnc/89f9N4mdL8V9DRn03Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.651.1.tgz", + "integrity": "sha512-PKwAyTJW8pgaPIXm708haIZWBAwNycs25yNcD7OQ3NLcmgGxvrx6bSlhPEGcvwdTYwQMJsdx8ls+khlYbLqTvQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/credential-provider-node": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.651.1" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sts": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.651.1.tgz", + "integrity": "sha512-4X2RqLqeDuVLk+Omt4X+h+Fa978Wn+zek/AM4HSPi4C5XzRBEFLRRtOQUvkETvIjbEwTYQhm0LdgzcBH4bUqIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.651.1", + "@aws-sdk/core": "3.651.1", + "@aws-sdk/credential-provider-node": "3.651.1", + "@aws-sdk/middleware-host-header": "3.649.0", + "@aws-sdk/middleware-logger": "3.649.0", + "@aws-sdk/middleware-recursion-detection": "3.649.0", + "@aws-sdk/middleware-user-agent": "3.649.0", + "@aws-sdk/region-config-resolver": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@aws-sdk/util-user-agent-browser": "3.649.0", + "@aws-sdk/util-user-agent-node": "3.649.0", + "@smithy/config-resolver": "^3.0.6", + "@smithy/core": "^2.4.1", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/hash-node": "^3.0.4", + "@smithy/invalid-dependency": "^3.0.4", + "@smithy/middleware-content-length": "^3.0.6", + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.16", + "@smithy/util-defaults-mode-node": "^3.0.16", + "@smithy/util-endpoints": "^2.1.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/core": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.651.1.tgz", + "integrity": "sha512-eqOq3W39K+5QTP5GAXtmP2s9B7hhM2pVz8OPe5tqob8o1xQgkwdgHerf3FoshO9bs0LDxassU/fUSz1wlwqfqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.1", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/property-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/signature-v4": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-middleware": "^3.0.4", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.649.0.tgz", + "integrity": "sha512-tViwzM1dauksA3fdRjsg0T8mcHklDa8EfveyiQKK6pUJopkqV6FQx+X5QNda0t/LrdEVlFZvwHNdXqOEfc83TA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.649.0.tgz", + "integrity": "sha512-ODAJ+AJJq6ozbns6ejGbicpsQ0dyMOpnGlg0J9J0jITQ05DKQZ581hdB8APDOZ9N8FstShP6dLZflSj8jb5fNA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-stream": "^3.1.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.651.1.tgz", + "integrity": "sha512-yOzPC3GbwLZ8IYzke4fy70ievmunnBUni/MOXFE8c9kAIV+/RMC7IWx14nAAZm0gAcY+UtCXvBVZprFqmctfzA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.649.0", + "@aws-sdk/credential-provider-http": "3.649.0", + "@aws-sdk/credential-provider-process": "3.649.0", + "@aws-sdk/credential-provider-sso": "3.651.1", + "@aws-sdk/credential-provider-web-identity": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.651.1" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.651.1.tgz", + "integrity": "sha512-QKA74Qs83FTUz3jS39kBuNbLAnm6cgDqomm7XS/BkYgtUq+1lI9WL97astNIuoYvumGIS58kuIa+I3ycOA4wgw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.649.0", + "@aws-sdk/credential-provider-http": "3.649.0", + "@aws-sdk/credential-provider-ini": "3.651.1", + "@aws-sdk/credential-provider-process": "3.649.0", + "@aws-sdk/credential-provider-sso": "3.651.1", + "@aws-sdk/credential-provider-web-identity": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.649.0.tgz", + "integrity": "sha512-6VYPQpEVpU+6DDS/gLoI40ppuNM5RPIEprK30qZZxnhTr5wyrGOeJ7J7wbbwPOZ5dKwta290BiJDU2ipV8Y9BQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.651.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.651.1.tgz", + "integrity": "sha512-7jeU+Jbn65aDaNjkjWDQcXwjNTzpYNKovkSSRmfVpP5WYiKerVS5mrfg3RiBeiArou5igCUtYcOKlRJiGRO47g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.651.1", + "@aws-sdk/token-providers": "3.649.0", + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.649.0.tgz", + "integrity": "sha512-XVk3WsDa0g3kQFPmnCH/LaCtGY/0R2NDv7gscYZSXiBZcG/fixasglTprgWSp8zcA0t7tEIGu9suyjz8ZwhymQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.649.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.649.0.tgz", + "integrity": "sha512-PjAe2FocbicHVgNNwdSZ05upxIO7AgTPFtQLpnIAmoyzMcgv/zNB5fBn3uAnQSAeEPPCD+4SYVEUD1hw1ZBvEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-logger": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.649.0.tgz", + "integrity": "sha512-qdqRx6q7lYC6KL/NT9x3ShTL0TBuxdkCczGzHzY3AnOoYUjnCDH7Vlq867O6MAvb4EnGNECFzIgtkZkQ4FhY5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.649.0.tgz", + "integrity": "sha512-IPnO4wlmaLRf6IYmJW2i8gJ2+UPXX0hDRv1it7Qf8DpBW+lGyF2rnoN7NrFX0WIxdGOlJF1RcOr/HjXb2QeXfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.649.0.tgz", + "integrity": "sha512-q6sO10dnCXoxe9thobMJxekhJumzd1j6dxcE1+qJdYKHJr6yYgWbogJqrLCpWd30w0lEvnuAHK8lN2kWLdJxJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@aws-sdk/util-endpoints": "3.649.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.649.0.tgz", + "integrity": "sha512-xURBvdQXvRvca5Du8IlC5FyCj3pkw8Z75+373J3Wb+vyg8GjD14HfKk1Je1HCCQDyIE9VB/scYDcm9ri0ppePw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/token-providers": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.649.0.tgz", + "integrity": "sha512-ZBqr+JuXI9RiN+4DSZykMx5gxpL8Dr3exIfFhxMiwAP3DQojwl0ub8ONjMuAjq9OvmX6n+jHZL6fBnNgnNFC8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.649.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.649.0.tgz", + "integrity": "sha512-PuPw8RysbhJNlaD2d/PzOTf8sbf4Dsn2b7hwyGh7YVG3S75yTpxSAZxrnhKsz9fStgqFmnw/jUfV/G+uQAeTVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-endpoints": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.649.0.tgz", + "integrity": "sha512-bZI1Wc3R/KibdDVWFxX/N4AoJFG4VJ92Dp4WYmOrVD6VPkb8jPz7ZeiYc7YwPl8NoDjYyPneBV0lEoK/V8OKAA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "@smithy/util-endpoints": "^2.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.649.0.tgz", + "integrity": "sha512-IY43r256LhKAvdEVQO/FPdUyVpcZS5EVxh/WHVdNzuN1bNLoUK2rIzuZqVA0EGguvCxoXVmQv9m50GvG7cGktg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/types": "^3.4.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.649.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.649.0.tgz", + "integrity": "sha512-x5DiLpZDG/AJmCIBnE3Xhpwy35QIo3WqNiOpw6ExVs1NydbM/e90zFPSfhME0FM66D/WorigvluBxxwjxDm/GA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.649.0", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.645.0.tgz", + "integrity": "sha512-RjT/mfNv4yr1uv/+aEXgSIxC5EB+yHPSU7hH0KZOZrvZEFASLl0i4FeoHzbMEOH5KdKGAi0uu3zRP3D1y45sKg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-bucket-endpoint": "3.620.0", + "@aws-sdk/middleware-expect-continue": "3.620.0", + "@aws-sdk/middleware-flexible-checksums": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-location-constraint": "3.609.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-sdk-s3": "3.635.0", + "@aws-sdk/middleware-ssec": "3.609.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/signature-v4-multi-region": "3.635.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@aws-sdk/xml-builder": "3.609.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-blob-browser": "^3.1.2", + "@smithy/hash-node": "^3.0.3", + "@smithy/hash-stream-node": "^3.1.2", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/md5-js": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.645.0.tgz", + "integrity": "sha512-bM0bgwVjrzpmbcsY8sWYW5JnSUwQMcM7+hrVA6bWXNkMAScwJit6fq0nmXBbHRcPDaRI5WPt8t6C9q1DIGW6eg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.645.0.tgz", + "integrity": "sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.645.0.tgz", + "integrity": "sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.645.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.645.0.tgz", + "integrity": "sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", + "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.645.0.tgz", + "integrity": "sha512-Z4By/90TaYQZO1dPR1udYhegFiOlSWnZsJOYSAk4Gdny26Tqb78xVLw9R/33CzFblXC4WVSt4gizXTQ/sYyHNg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.645.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", + "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.645.0.tgz", + "integrity": "sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.645.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.645.0.tgz", + "integrity": "sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.645.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.645.0.tgz", + "integrity": "sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.645.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.645.0.tgz", + "integrity": "sha512-6g9qMngrMCvHNsxmh/1urnWKrvaa2fv55b3bYwPxwJCYAvg/xc7bV8YHL7GS2rJpACG707k9G86DTW+Hab8bJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.645.0", + "@aws-sdk/client-sso": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/credential-provider-cognito-identity": "3.645.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.645.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", + "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-arn-parser": "3.568.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", + "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", + "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-sdk/types": "3.609.0", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz", + "integrity": "sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", + "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.635.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-arn-parser": "3.568.0", + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", + "integrity": "sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.645.0.tgz", + "integrity": "sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.645.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", + "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.635.0", + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", + "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.645.0.tgz", + "integrity": "sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz", + "integrity": "sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws/agents-for-amazon-bedrock-blueprints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@aws/agents-for-amazon-bedrock-blueprints/-/agents-for-amazon-bedrock-blueprints-1.0.0.tgz", + "integrity": "sha512-+fFuCUhDa5VD1rTMYx2gSdopX1rUlFcECu7fhdrdwaE59eQkNjQXmHtvwsf+GumVCwwSOLNoZecqG5Xrg3Eu9g==", + "license": "MIT-0", + "dependencies": { + "@aws-lambda-powertools/logger": "^2.6.0", + "@aws-sdk/client-bedrock-agent": "^3.529.0", + "@aws-sdk/client-lambda": "^3.529.0", + "@aws-sdk/client-secrets-manager": "^3.529.0", + "@aws-sdk/credential-providers": "^3.529.0", + "@opensearch-project/opensearch": "^2.6.0", + "source-map-support": "^0.5.21", + "ts-retry": "^4.2.5", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.140.0", + "constructs": "^10.0.0", + "esbuild": "^0.23.0" + } + }, + "node_modules/@aws/agents-for-amazon-bedrock-blueprints/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@cdklabs/generative-ai-cdk-constructs": { + "version": "0.1.264", + "resolved": "https://registry.npmjs.org/@cdklabs/generative-ai-cdk-constructs/-/generative-ai-cdk-constructs-0.1.264.tgz", + "integrity": "sha512-17RTt5bh6AIoN3N9WwhTvsmFRF3DQQxFM/KO+rYkuBaM9C2UIiPHij7zgSbbP+q65+OUiNNi0euTyqSF7kRPoA==", + "bundleDependencies": [ + "deepmerge" + ], + "license": "Apache-2.0", + "dependencies": { + "cdk-nag": "^2.28.195", + "deepmerge": "^4.3.1" + }, + "engines": { + "node": ">= 18.12.0 <= 20.x" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.154.1", + "constructs": "^10.3.0" + } + }, + "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/deepmerge": { + "version": "4.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@opensearch-project/opensearch": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-2.12.0.tgz", + "integrity": "sha512-FNGWbWjvpWIZHVvAbv0FkSgvc1PnWnYEHnOTeIY08vMDp9QpXumGNDjNc1tZthJ3OEeoooqH0miGFORjWnRYsQ==", + "license": "Apache-2.0", + "dependencies": { + "aws4": "^1.11.0", + "debug": "^4.3.1", + "hpagent": "^1.2.0", + "json11": "^1.1.2", + "ms": "^2.1.3", + "secure-json-parse": "^2.4.0" + }, + "engines": { + "node": ">=10", + "yarn": "^1.22.10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.4.tgz", + "integrity": "sha512-VupaALAQlXViW3/enTf/f5l5JZYSAxoJL7f0nanhNNKnww6DGCg1oYIuNP78KDugnkwthBO6iEcym16HhWV8RQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz", + "integrity": "sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz", + "integrity": "sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.6.tgz", + "integrity": "sha512-j7HuVNoRd8EhcFp0MzcUb4fG40C7BcyshH+fAd3Jhd8bINNFvEQYBrZoS/SK6Pun9WPlfoI8uuU2SMz8DsEGlA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.1.tgz", + "integrity": "sha512-7cts7/Oni7aCHebHGiBeWoz5z+vmH+Vx2Z/UW3XtXMslcxI3PEwBZxNinepwZjixS3n12fPc247PHWmjU7ndsQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-retry": "^3.0.16", + "@smithy/middleware-serde": "^3.0.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.1.tgz", + "integrity": "sha512-4z/oTWpRF2TqQI3aCM89/PWu3kim58XU4kOCTtuTJnoaS4KT95cPWMxbQfTN2vzcOe96SOKO8QouQW/+ESB1fQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.5", + "@smithy/property-provider": "^3.1.4", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.5.tgz", + "integrity": "sha512-6pu+PT2r+5ZnWEV3vLV1DzyrpJ0TmehQlniIDCSpZg6+Ji2SfOI38EqUyQ+O8lotVElCrfVc9chKtSMe9cmCZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.4.2", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.9.tgz", + "integrity": "sha512-PiQLo6OQmZAotJweIcObL1H44gkvuJACKMNqpBBe5Rf2Ax1DOcGi/28+feZI7yTe1ERHlQQaGnm8sSkyDUgsMg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.8", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.6.tgz", + "integrity": "sha512-iew15It+c7WfnVowWkt2a7cdPp533LFJnpjDQgfZQcxv2QiOcyEcea31mnrk5PVbgo0nNH3VbYGq7myw2q/F6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.8.tgz", + "integrity": "sha512-6m+wI+fT0na+6oao6UqALVA38fsScCpoG5UO/A8ZSyGLnPM2i4MS1cFUhpuALgvLMxfYoTCh7qSeJa0aG4IWpQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.8", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.8.tgz", + "integrity": "sha512-09tqzIQ6e+7jLqGvRji1yJoDbL/zob0OFhq75edgStWErGLf16+yI5hRc/o9/YAybOhUZs/swpW2SPn892G5Gg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.5", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.5.tgz", + "integrity": "sha512-DjRtGmK8pKQMIo9+JlAKUt14Z448bg8nAN04yKIvlrrpmpRSG57s5d2Y83npks1r4gPtTRNbAFdQCoj9l3P2KQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.1", + "@smithy/querystring-builder": "^3.0.4", + "@smithy/types": "^3.4.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz", + "integrity": "sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^3.0.0", + "@smithy/chunked-blob-reader-native": "^3.0.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.4.tgz", + "integrity": "sha512-6FgTVqEfCr9z/7+Em8BwSkJKA2y3krf1em134x3yr2NHWVCo2KYI8tcA53cjeO47y41jwF84ntsEE0Pe6pNKlg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz", + "integrity": "sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.4.tgz", + "integrity": "sha512-MJBUrojC4SEXi9aJcnNOE3oNAuYNphgCGFXscaCj2TA/59BTcXhzHACP8jnnEU3n4yir/NSLKzxqez0T4x4tjA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", + "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.6.tgz", + "integrity": "sha512-AFyHCfe8rumkJkz+hCOVJmBagNBj05KypyDwDElA4TgMSA4eYDZRjVePFZuyABrJZFDc7uVj3dpFIDCEhf59SA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.1.tgz", + "integrity": "sha512-Irv+soW8NKluAtFSEsF8O3iGyLxa5oOevJb/e1yNacV9H7JP/yHyJuKST5YY2ORS1+W34VR8EuUrOF+K29Pl4g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.4", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "@smithy/url-parser": "^3.0.4", + "@smithy/util-middleware": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.16.tgz", + "integrity": "sha512-08kI36p1yB4CWO3Qi+UQxjzobt8iQJpnruF0K5BkbZmA/N/sJ51A1JJGJ36GgcbFyPfWw2FU48S5ZoqXt0h0jw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.5", + "@smithy/protocol-http": "^4.1.1", + "@smithy/service-error-classification": "^3.0.4", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-retry": "^3.0.4", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.4.tgz", + "integrity": "sha512-1lPDB2O6IJ50Ucxgn7XrvZXbbuI48HmPCcMTuSoXT1lDzuTUfIuBjgAjpD8YLVMfnrjdepi/q45556LA51Pubw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.4.tgz", + "integrity": "sha512-sLMRjtMCqtVcrOqaOZ10SUnlFE25BSlmLsi4bRSGFD7dgR54eqBjfqkVkPBQyrKBortfGM0+2DJoUPcGECR+nQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", + "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.0.tgz", + "integrity": "sha512-5TFqaABbiY7uJMKbqR4OARjwI/l4TRoysDJ75pLpVQyO3EcmeloKYwDGyCtgB9WJniFx3BMkmGCB9+j+QiB+Ww==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.2", + "@smithy/protocol-http": "^4.1.1", + "@smithy/querystring-builder": "^3.0.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.4.tgz", + "integrity": "sha512-BmhefQbfkSl9DeU0/e6k9N4sT5bya5etv2epvqLUz3eGyfRBhtQq60nDkc1WPp4c+KWrzK721cUc/3y0f2psPQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.1.tgz", + "integrity": "sha512-Fm5+8LkeIus83Y8jTL1XHsBGP8sPvE1rEVyKf/87kbOPTbzEDMcgOlzcmYXat2h+nC3wwPtRy8hFqtJS71+Wow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.4.tgz", + "integrity": "sha512-NEoPAsZPdpfVbF98qm8i5k1XMaRKeEnO47CaL5ja6Y1Z2DgJdwIJuJkTJypKm/IKfp8gc0uimIFLwhml8+/pAw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.4.tgz", + "integrity": "sha512-7CHPXffFcakFzhO0OZs/rn6fXlTHrSDdLhIT6/JIk1u2bvwguTL3fMCc1+CfcbXA7TOhjWXu3TcB1EGMqJQwHg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.4.tgz", + "integrity": "sha512-KciDHHKFVTb9A1KlJHBt2F26PBaDtoE23uTZy5qRvPzHPqrooXFi6fmx98lJb3Jl38PuUTqIuCUmmY3pacuMBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", + "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.1.tgz", + "integrity": "sha512-SH9J9be81TMBNGCmjhrgMWu4YSpQ3uP1L06u/K9SDrE2YibUix1qxedPCxEQu02At0P0SrYDjvz+y91vLG0KRQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.4", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.0.tgz", + "integrity": "sha512-H32nVo8tIX82kB0xI2LBrIcj8jx/3/ITotNLbeG1UL0b3b440YPR/hUvqjFJiaB24pQrMjRbU8CugqH5sV0hkw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.1", + "@smithy/middleware-stack": "^3.0.4", + "@smithy/protocol-http": "^4.1.1", + "@smithy/types": "^3.4.0", + "@smithy/util-stream": "^3.1.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.2.tgz", + "integrity": "sha512-tHiFcfcVedVBHpmHUEUHOCCih8iZbIAYn9NvPsNzaPm/237I3imdDdZoOC8c87H5HBAVEa06tTgb+OcSWV9g5w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.4.tgz", + "integrity": "sha512-XdXfObA8WrloavJYtDuzoDhJAYc5rOt+FirFmKBRKaihu7QtU/METAxJgSo7uMK6hUkx0vFnqxV75urtRaLkLg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.16.tgz", + "integrity": "sha512-Os8ddfNBe7hmc5UMWZxygIHCyAqY0aWR8Wnp/aKbti3f8Df/r0J9ttMZIxeMjsFgtVjEryB0q7SGcwBsHk8WEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.4", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.16.tgz", + "integrity": "sha512-rNhFIYRtrOrrhRlj6RL8jWA6/dcwrbGYAmy8+OAHjjzQ6zdzUBB1P+3IuJAgwWN6Y5GxI+mVXlM/pOjaoIgHow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.6", + "@smithy/credential-provider-imds": "^3.2.1", + "@smithy/node-config-provider": "^3.1.5", + "@smithy/property-provider": "^3.1.4", + "@smithy/smithy-client": "^3.3.0", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.0.tgz", + "integrity": "sha512-ilS7/0jcbS2ELdg0fM/4GVvOiuk8/U3bIFXUW25xE1Vh1Ol4DP6vVHQKqM40rCMizCLmJ9UxK+NeJrKlhI3HVA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.5", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.4.tgz", + "integrity": "sha512-uSXHTBhstb1c4nHdmQEdkNMv9LiRNaJ/lWV2U/GO+5F236YFpdPw+hyWI9Zc0Rp9XKzwD9kVZvhZmEgp0UCVnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.4.tgz", + "integrity": "sha512-JJr6g0tO1qO2tCQyK+n3J18r34ZpvatlFN5ULcLranFIBZPxqoivb77EPyNTVwTGMEvvq2qMnyjm4jMIxjdLFg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.4", + "@smithy/types": "^3.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.4.tgz", + "integrity": "sha512-txU3EIDLhrBZdGfon6E9V6sZz/irYnKFMblz4TLVjyq8hObNHNS2n9a2t7GIrl7d85zgEPhwLE0gANpZsvpsKg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.5", + "@smithy/node-http-handler": "^3.2.0", + "@smithy/types": "^3.4.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.5.tgz", + "integrity": "sha512-jYOSvM3H6sZe3CHjzD2VQNCjWBJs+4DbtwBMvUp9y5EnnwNa7NQxTeYeQw0CKCAdGGZ3QvVkyJmvbvs5M/B10A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.4", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/archiver": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.2.tgz", + "integrity": "sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/readdir-glob": "*" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.145", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", + "integrity": "sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", + "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/readdir-glob": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-cdk": { + "version": "2.154.1", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.154.1.tgz", + "integrity": "sha512-yJoLTo+fUHRLD4YQMt/QoOPgiT/daci4I5KcaDK8Cx2fWA0Z3h5U9+bWS3ah+8OeZ91fciNCwt6Yt/0p+cp2GQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.154.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.154.1.tgz", + "integrity": "sha512-XV04/XyNKJ2yyMfYsiSmWx+rIKwTrcrd87p61t4xhE240Iy6Y6LxXVdvkNEOjjbeXVmOUQ7JBG9cW1BeeFiDgg==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml", + "mime-types" + ], + "license": "Apache-2.0", + "dependencies": { + "@aws-cdk/asset-awscli-v1": "^2.2.202", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.3", + "@aws-cdk/cloud-assembly-schema": "^36.0.5", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.2.0", + "ignore": "^5.3.1", + "jsonschema": "^1.4.1", + "mime-types": "^2.1.35", + "minimatch": "^3.1.2", + "punycode": "^2.3.1", + "semver": "^7.6.2", + "table": "^6.8.2", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.16.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.6.2", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.2", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/aws-lambda": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/aws-lambda/-/aws-lambda-1.0.7.tgz", + "integrity": "sha512-9GNFMRrEMG5y3Jvv+V4azWvc+qNWdWLTjDdhf/zgMlz8haaaLWv0xeAIWxz9PuWUBawsVxy0zZotjCdR3Xq+2w==", + "license": "MIT", + "dependencies": { + "aws-sdk": "^2.814.0", + "commander": "^3.0.2", + "js-yaml": "^3.14.1", + "watchpack": "^2.0.0-beta.10" + }, + "bin": { + "lambda": "bin/lambda" + } + }, + "node_modules/aws-sdk": { + "version": "2.1691.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1691.0.tgz", + "integrity": "sha512-/F2YC+DlsY3UBM2Bdnh5RLHOPNibS/+IcjUuhP8XuctyrN+MlL+fWDAiela32LTDk7hMy4rx8MTgvbJ+0blO5g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "license": "Apache-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", + "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdk-nag": { + "version": "2.28.195", + "resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.28.195.tgz", + "integrity": "sha512-5kVN9pX14phyzY55xJAKZqkeGW6kd0WpV2o/hdGjwAFIeYwte3W2xhKsqZV/IF8HarhsvRrnoz3JI085LeqRGg==", + "license": "Apache-2.0", + "peerDependencies": { + "aws-cdk-lib": "^2.116.0", + "constructs": "^10.0.5" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/constructs": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", + "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 16.14.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hpagent": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json11": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/json11/-/json11-1.1.2.tgz", + "integrity": "sha512-5r1RHT1/Gr/jsI/XZZj/P6F11BKM8xvTaftRuiLkQI9Z2PFDukM82Ysxw8yDszb3NJP/NKnRlSGmhUdG99rlBw==", + "license": "MIT", + "bin": { + "json11": "dist/cli.mjs" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pdf2json": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/pdf2json/-/pdf2json-3.1.4.tgz", + "integrity": "sha512-rS+VapXpXZr+5lUpHmRh3ugXdFXp24p1RyG24yP1DMpqP4t0mrYNGpLtpSbWD42PnQ59GIXofxF+yWb7M+3THg==", + "bundleDependencies": [ + "@xmldom/xmldom" + ], + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "^0.8.10" + }, + "bin": { + "pdf2json": "bin/pdf2json.js" + }, + "engines": { + "node": ">=18.12.1", + "npm": ">=8.19.2" + } + }, + "node_modules/pdf2json/node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/pdfreader": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/pdfreader/-/pdfreader-3.0.5.tgz", + "integrity": "sha512-1xuKu/WakHbmobORf+xJd+n77A5JhHgojvDBYq/wkhxrAy2nr0/049AEeZFpQ/mUUPVu+6gHgKmgWg74RpDYuQ==", + "license": "MIT", + "dependencies": { + "pdf2json": "3.1.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/readable-stream/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/readable-stream/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/streamx": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-retry": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/ts-retry/-/ts-retry-4.2.5.tgz", + "integrity": "sha512-dFBa4pxMBkt/bjzdBio8EwYfbAdycEAwe0KZgzlUKKwU9Wr1WErK7Hg9QLqJuDDYJXTW4KYZyXAyqYKOdO/ehA==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/package.json b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/package.json new file mode 100644 index 00000000..8f50aaac --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/package.json @@ -0,0 +1,39 @@ +{ + "name": "cdk-genai-app", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "cdk": "cdk", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@types/archiver": "^6.0.2", + "@types/aws-lambda": "^8.10.145", + "@types/node": "^22.5.0", + "aws-cdk": "^2.154.1", + "aws-cdk-lib": "^2.154.1", + "constructs": "^10.3.0", + "ts-node": "^10.9.2", + "typescript": "^5.5.4" + }, + "dependencies": { + "@aws-sdk/client-bedrock-agent-runtime": "^3.651.1", + "@aws-sdk/client-bedrock-runtime": "^3.645.0", + "@aws-sdk/client-codepipeline": "^3.650.0", + "@aws-sdk/client-lambda": "^3.651.1", + "@aws-sdk/client-s3": "^3.645.0", + "@aws/agents-for-amazon-bedrock-blueprints": "^1.0.0", + "@cdklabs/generative-ai-cdk-constructs": "^0.1.264", + "archiver": "^7.0.1", + "aws-lambda": "^1.0.7", + "aws-sdk": "^2.1691.0", + "esbuild": "^0.23.1", + "pdfreader": "^3.0.5" + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/Dockerfile b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/Dockerfile new file mode 100644 index 00000000..e3c8b21e --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/Dockerfile @@ -0,0 +1,19 @@ +# FROM public.ecr.aws/docker/library/python:3.10-slim +FROM --platform=linux/amd64 public.ecr.aws/docker/library/python:3.10-slim + +RUN apt-get update && apt-get install -y \ + build-essential \ + software-properties-common \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt ./requirements.txt + +RUN pip3 install --upgrade pip && pip3 install -r requirements.txt + +WORKDIR /app + +COPY /app . + +EXPOSE 8501 + +ENTRYPOINT [ "streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0" ] \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/CustomChunker/custom_chunking_lambda_function.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/CustomChunker/custom_chunking_lambda_function.py new file mode 100644 index 00000000..e376e3df --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/CustomChunker/custom_chunking_lambda_function.py @@ -0,0 +1,118 @@ +import json +import logging +from abc import ABC, abstractmethod +from typing import List +from urllib.parse import urlparse +import boto3 + +# Set up logging +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +# Abstract class for chunking text +class Chunker(ABC): + @abstractmethod + def chunk(self, text: str) -> List[str]: + raise NotImplementedError() + +# Simple chunker implementation with 1000-word chunks +class SimpleChunker(Chunker): + def chunk(self, text: str) -> List[str]: + words = text.split() + return [' '.join(words[i:i + 1000]) for i in range(0, len(words), 1000)] + +def lambda_handler(event, context): + logger.debug(f'Input event to custom chunker lambda: {json.dumps(event)}') + + # Initialize S3 client + s3 = boto3.client('s3') + + # Extract input parameters from the event + input_files = event.get('inputFiles') + input_bucket = event.get('bucketName') + + if not all([input_files, input_bucket]): + raise ValueError("Missing required input parameters") + + chunker = SimpleChunker() + output_files = [] + + # Process each input file + for input_file in input_files: + content_batches = input_file.get('contentBatches', []) + file_metadata = input_file.get('fileMetadata', {}) + original_file_location = input_file.get('originalFileLocation', {}) + + processed_batches = [] + + # Process each content batch + for batch in content_batches: + input_key = batch.get('key') + + if not input_key: + raise ValueError("Missing 'key' in content batch") + + # Read the file from S3 + file_content = read_s3_file(s3, input_bucket, input_key) + + # Chunk the content using SimpleChunker + chunked_content = process_content(file_content, chunker) + + # Define the output key and write the chunked content back to S3 + output_key = f"Output/{input_key}" + write_to_s3(s3, input_bucket, output_key, chunked_content) + + # Add batch information for tracking + processed_batches.append({'key': output_key}) + + # Prepare the output file information + output_file = { + 'originalFileLocation': original_file_location, + 'fileMetadata': file_metadata, + 'contentBatches': processed_batches + } + output_files.append(output_file) + + # Return the result with all output files + result = {'outputFiles': output_files} + logger.debug(f'Result: {json.dumps(result)}') + + return result + +# Helper function to read content from S3 +def read_s3_file(s3_client, bucket: str, key: str) -> dict: + response = s3_client.get_object(Bucket=bucket, Key=key) + return json.loads(response['Body'].read().decode('utf-8')) + +# Helper function to write content to S3 +def write_to_s3(s3_client, bucket: str, key: str, content: dict): + s3_client.put_object(Bucket=bucket, Key=key, Body=json.dumps(content)) + +# Process file content by chunking it +def process_content(file_content: dict, chunker: Chunker) -> dict: + chunked_content = {'fileContents': []} + + for content in file_content.get('fileContents', []): + content_body = content.get('contentBody', '') + content_type = content.get('contentType', '') + content_metadata = content.get('contentMetadata', {}) + + # Create chunks using the chunker + chunks = chunker.chunk(content_body) + + # Store each chunk as a separate content item + for chunk in chunks: + chunked_content['fileContents'].append({ + 'contentType': content_type, + 'contentMetadata': content_metadata, + 'contentBody': chunk + }) + + return chunked_content + +# Helper function to extract bucket and key from S3 URI +def extract_bucket_and_key(s3_uri: str): + parsed_url = urlparse(s3_uri) + bucket = parsed_url.netloc + key = parsed_url.path.lstrip('/') + return bucket, key diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/app.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/app.py new file mode 100644 index 00000000..6dfcdf6e --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/app.py @@ -0,0 +1,175 @@ +import requests +import streamlit as st +import json +import os +import base64 +from PIL import Image +from io import BytesIO +import logging + +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +# Set the title with an enterprise-friendly emoji and styling +st.markdown("

💼 Octank Financial Generative AI Assistant

", unsafe_allow_html=True) + +# Add a formal description or instructions +st.write("Welcome to the Octank Financial Generative AI-powered solution.") + +# Add a separator +st.markdown("---") + +# Input text area for the user with a placeholder +st.markdown("#### 📝 Ask Your Query") +user_input = st.text_area("User Input", "", label_visibility="collapsed") + +# Dropdown for selecting the action +action = st.selectbox( + "Select Task:", + options=[ "Ask Knowledge Base", "Generate with Foundation Model"], + help="Choose whether to generate response using the knowledge base or directly generate it using the model." +) + + +# Add a button with a subtle icon and formal style +if st.button('Submit Request'): + # Get the API Gateway endpoint + api_gateway_url = os.getenv("APIGATEWAY_ENDPOINT", "") + "/invoke" + + # Check for errors in input or environment variables + if not api_gateway_url: + st.error("⚠️ API Gateway URL is not set in the environment.") + elif not user_input.strip(): + st.error("⚠️ Please enter a query.") + else: + # Call the API Gateway + try: + # API call + response = requests.post(api_gateway_url, json={'prompt': user_input, 'action': "model" if action == "Run Model Task" else "knowledge"}) + + # Handle the API response + if response.status_code == 200: + data = response.json() + # print("data from API Gateway:", data) + # logger.info("data from API Gateway:", data) + assistant_response = data.get('generatedResponse', 'No response available') + logger.info(f"Assistant response: {assistant_response}") + + citations = data.get('citations', []) + # print("citations:", citations) + logger.info(f"References: {citations}") + + # Display assistant's response with a success box + # st.success("Response:") + # st.write(assistant_response) + + # Display assistant's response in a special box + # Display assistant's response in a special box with custom font + st.markdown(f""" +
+ Response: +

{assistant_response}

+
+ """, unsafe_allow_html=True) + + # Add a separator + st.markdown("---") + + # Display references, if available + if citations: + st.markdown("### 📄 Supporting References") + + # CSS for multi-column scrollable references + st.markdown(""" + + """, unsafe_allow_html=True) + + # Create the reference container + st.markdown('
', unsafe_allow_html=True) + + # for i, citation in enumerate(citations): + # print("/n i:", i) + # references = citations.get('retrievedReferences', []) + + + print("citations before loop:", citations) + k = 0 + # Iterate through each citation + for citation in citations: + + + # Get the generated response part (optional, for context) + generated_text = citation['generatedResponsePart']['textResponsePart']['text'] + print(f"Generated Response: {generated_text}") + + # Retrieve the references + references = citation.get('retrievedReferences', []) + print(f"References: {references}") + + # Loop through the references and print the content + if references: + k += 1 + + for j, ref in enumerate(references): + # Begin each reference block + # st.markdown('
', unsafe_allow_html=True) + + # Display the reference title and content using markdown to allow HTML rendering + st.markdown(f"**Reference :** {ref.get('content', {}).get('text', 'No content available')}", unsafe_allow_html=True) + + # Display the location information using markdown to allow HTML rendering + location = ref.get('location', {}).get('s3Location', {}).get('uri', 'No location available') + st.markdown(f"

Source Location: {location}

", unsafe_allow_html=True) + + # Display the image if metadata contains it + base64_image = ref.get('metadata', {}).get('base64Image') + if base64_image: + try: + image_data = base64.b64decode(base64_image) + image = Image.open(BytesIO(image_data)) + st.image(image, caption=f"Image from Reference {j+1}", use_column_width=True) + except Exception as e: + st.error(f"Error displaying image: {str(e)}") + + # End each reference block + st.markdown('
', unsafe_allow_html=True) + + # Close the reference container + st.markdown('
', unsafe_allow_html=True) + + else: + st.error(f"API Error: {response.status_code} - {response.text}") + + except Exception as e: + st.error(f"Error calling the API: {str(e)}") + + +# if st.checkbox("Show Logs"): +# st.text_area("Logs", value=open("streamlit_app.log").read(), height=200) + +# Add a footer or custom message at the bottom of the app +st.markdown("
Powered by Octank Financial AI Solutions
", unsafe_allow_html=True) \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh new file mode 100644 index 00000000..dbbe6c04 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/build_lambda.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status +set -e + +# Check if the required environment variables are set +if [ -z "$CODE_PIPELINE_NAME" ] || [ -z "$STAGE_NAME" ]; then + echo "Error: CODE_PIPELINE_NAME or STAGE_NAME environment variable is not set." + exit 1 +fi + +# Construct the exact SSM parameter name +PARAMETER_NAME="/${CODE_PIPELINE_NAME}/${STAGE_NAME}/lambda-package-bucket-name" +REGION="${AWS_REGION}" + +echo "Retrieving S3 bucket name from SSM parameter: $PARAMETER_NAME in region $REGION" + +# Retrieve the S3 bucket name from SSM Parameter Store +LAMBDA_PACKAGE_BUCKET=$(aws ssm get-parameter --name "$PARAMETER_NAME" --region "$REGION" --query "Parameter.Value" --output text) + +if [ -z "$LAMBDA_PACKAGE_BUCKET" ]; then + echo "Error: Could not retrieve the Lambda package bucket name from SSM." + exit 1 +fi + +echo "Using S3 bucket: $LAMBDA_PACKAGE_BUCKET" +echo "Current working directory: $(pwd)" +echo "File contents:" +ls -ltr + + +# Variables +SRC_DIR="./rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/app/CustomChunker" +TMP_DIR="/tmp/my-lambda-package" # Use /tmp for temporary storage +ZIP_FILE="$TMP_DIR/lambda.zip" +S3_KEY="custom_chunking_lambda_package.zip" + +# Step 1: Prepare the temp directory +echo "Cleaning up old package..." +rm -rf "$TMP_DIR" # Remove old temp folder if it exists +mkdir -p "$TMP_DIR" # Create a new temp folder + +# Step 2: Copy the Python script and dependencies to the temp folder +echo "Copying Python code to temp directory..." +cp -R "$SRC_DIR"/* "$TMP_DIR/" + +# Removed the dependency installation step as it is not required for this Lambda function +# # Step 3: Install dependencies in the temp folder +# echo "Installing dependencies..." +# pip install pypdf -t "$TMP_DIR" --quiet + +# Step 4: Create the zip package +echo "Creating Lambda package zip file..." +cd "$TMP_DIR" +if zip -r "$ZIP_FILE" ./*; then + echo "Lambda package created successfully at: $ZIP_FILE" +else + echo "Error: Failed to create the Lambda package zip file." + exit 1 +fi + +# Step 5: Upload the zip package to the specified S3 bucket +echo "Uploading Lambda package to S3 bucket: $LAMBDA_PACKAGE_BUCKET" +if aws s3 cp "$ZIP_FILE" "s3://$LAMBDA_PACKAGE_BUCKET/$S3_KEY"; then + echo "Lambda package uploaded successfully to: s3://$LAMBDA_PACKAGE_BUCKET/$S3_KEY" +else + echo "Error: Failed to upload the Lambda package to S3." + exit 1 +fi diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/requirements.txt b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/requirements.txt new file mode 100644 index 00000000..fe436ded --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/requirements.txt @@ -0,0 +1,3 @@ +boto3 +streamlit +pillow \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/call-bedrock-lambda.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/call-bedrock-lambda.ts new file mode 100644 index 00000000..f817d100 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/call-bedrock-lambda.ts @@ -0,0 +1,211 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; +import { BedrockRuntimeClient, InvokeModelCommand, InvokeModelCommandInput, InvokeModelCommandOutput } from "@aws-sdk/client-bedrock-runtime"; +import { BedrockAgentRuntimeClient, RetrieveAndGenerateCommand, RetrieveAndGenerateCommandInput, RetrieveAndGenerateCommandOutput } from "@aws-sdk/client-bedrock-agent-runtime"; + +const awsRegion = process.env.AWS_REGION +const modelID = process.env.MODEL_ID; +// Knowledge base ID for BedrockAgentRuntimeClient +const knowledgeBaseId = process.env.KNOWLEDGE_BASE_ID; +console.log("Knowledge Base ID:", knowledgeBaseId); + +if (!modelID) throw new Error('MODEL_ID environment variable is missing.'); +if (!knowledgeBaseId) throw new Error('KNOWLEDGE_BASE_ID environment variable is missing.'); + +// Create instances of both Bedrock clients +const runtimeClient = new BedrockRuntimeClient({ region: awsRegion }); +const agentClient = new BedrockAgentRuntimeClient({ region: awsRegion }); + + +interface Reference { + content: { + text: string; + }; + location: { + s3Location: { + bucket: string; + key: string; + }; + type: string; + }; + metadata: { + 'x-amz-bedrock-kb-source-uri': string; + 'x-amz-bedrock-kb-chunk-id': string; + 'x-amz-bedrock-kb-data-source-id': string; + }; +} + +interface Citation { + generatedResponsePart: string; + retrievedReferences: Reference[]; +} + + +// Function to query the knowledge base +async function queryKnowledgeBase(prompt: string): Promise { + const input: RetrieveAndGenerateCommandInput = { + input: { + text: prompt, + }, + retrieveAndGenerateConfiguration: { + type: "KNOWLEDGE_BASE", + knowledgeBaseConfiguration: { + knowledgeBaseId: knowledgeBaseId, + modelArn: `arn:aws:bedrock:${awsRegion}::foundation-model/${modelID}`, + retrievalConfiguration: { + vectorSearchConfiguration: { + numberOfResults: 5, + }, + }, + }, + }, + }; + + console.log("Input to Retrieve and Generate:", JSON.stringify(input)); + const command: RetrieveAndGenerateCommand = new RetrieveAndGenerateCommand(input); + const response: RetrieveAndGenerateCommandOutput = await agentClient.send(command); + console.log("Response generated after sending the command:", response); + return response.output?.text as string; +} + +// Function to query the knowledge base with citation extraction +async function queryKnowledgeBaseWithCitations(prompt: string): Promise<{ responseText: string, citations: any[] }> { + const input: RetrieveAndGenerateCommandInput = { + input: { + text: prompt, + }, + retrieveAndGenerateConfiguration: { + type: "KNOWLEDGE_BASE", + knowledgeBaseConfiguration: { + knowledgeBaseId: knowledgeBaseId, + modelArn: `arn:aws:bedrock:${awsRegion}::foundation-model/${modelID}`, + retrievalConfiguration: { + vectorSearchConfiguration: { + numberOfResults: 5, + }, + }, + }, + }, + }; + + console.log("Input to Retrieve and Generate:", JSON.stringify(input)); + const command: RetrieveAndGenerateCommand = new RetrieveAndGenerateCommand(input); + const response: RetrieveAndGenerateCommandOutput = await agentClient.send(command); + console.log("Response generated after sending the command:", response); + + // Extract citations and response text + const responseText = response.output?.text || ''; + const citations = response.citations || []; + + return { responseText, citations }; +} + + +// Function to invoke a model using BedrockRuntimeClient +async function invokeModel(prompt: string): Promise { + const payload: InvokeModelCommandInput = { + modelId: modelID, + contentType: "application/json", + accept: "application/json", + body: JSON.stringify({ + anthropic_version: "bedrock-2023-05-31", + max_tokens: 1000, + messages: [ + { + role: "user", + content: [{ type: "text", text: prompt }], + }, + ], + }), + }; + + console.log("Payload for model invocation:", JSON.stringify(payload)); + + const apiResponse = await runtimeClient.send(new InvokeModelCommand(payload)); + const decodedResponseBody = new TextDecoder().decode(apiResponse.body); // Decode the response body + const responseBody = JSON.parse(decodedResponseBody); // Parse the response + const finalResponse = responseBody.content[0].text; // Extract the final response text + + console.log("Model Response:", finalResponse); + + return finalResponse; +} + +// Main Lambda handler +export const handler = async (event: APIGatewayProxyEvent): Promise => { + console.log("Event received:", JSON.stringify(event, null, 2)); + + // Extract input parameters from query string or body + const queryStringParameters = event.queryStringParameters || {}; + let prompt = queryStringParameters.prompt || 'Hi'; + let action = queryStringParameters.action || 'knowledge'; // Default to 'knowledge' if no action is provided + + // If the HTTP method is POST, parse the body + if (event.httpMethod === 'POST' && event.body) { + try { + const body = JSON.parse(event.body); + prompt = body.prompt || prompt; + action = body.action || action; + } catch (error) { + console.error('Error parsing body:', error); + } + } + + console.log(`Action: ${action}`); + console.log(`Prompt: ${prompt}`); + + try { + let result: string; + let citations: any[] = []; + + // Choose the correct function based on the action + if (action === 'knowledge') { + console.log("Querying the knowledge base..."); + // result = await queryKnowledgeBase(prompt); + const knowledgebaseResult = await queryKnowledgeBaseWithCitations(prompt); + console.log("Knowledge Result:", knowledgebaseResult); + + result = knowledgebaseResult.responseText; + citations = knowledgebaseResult.citations; + + console.log("knowledgebaseResult response:", result); + console.log("knowledgebaseResult citations:", citations); + + // Unpack and log citations + citations.forEach((citation: Citation, index: number) => { + console.log(`Citation ${index + 1}:`); + console.log("Generated Response Part:", citation.generatedResponsePart); + + // Unpack and log retrieved references + citation.retrievedReferences.forEach((reference: Reference, refIndex: number) => { + console.log(` Retrieved Reference ${refIndex + 1}:`); + console.log(" Content Text:", reference.content.text); + console.log(" Location:", reference.location); + console.log(" Metadata:", reference.metadata); + }); + }); + + } else { + console.log("Invoking the model..."); + result = await invokeModel(prompt); + } + + return { + statusCode: 200, + body: JSON.stringify({ + generatedResponse: result, + citations: citations, // Return citations if available + }), + }; + + } catch (err) { + console.error("Error occurred:", err); + + return { + statusCode: 500, + body: JSON.stringify({ + generatedResponse: 'An error occurred', + error: err instanceof Error ? err.message : 'Unknown error', + }), + }; + } +}; diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/check-for-new-files.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/check-for-new-files.ts new file mode 100644 index 00000000..7a996c94 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/check-for-new-files.ts @@ -0,0 +1,59 @@ +import { DynamoDB } from "aws-sdk"; + +const dynamodbClient = new DynamoDB.DocumentClient(); +const FILE_METADATA_TABLE = process.env.FILE_METADATA_TABLE; + +// Lambda handler to check for file modifications (new files added or existing files deleted) +export const handler = async (): Promise => { + try { + // Query the DynamoDB GSI for new files added + const newFilesResult = await dynamodbClient.query({ + TableName: FILE_METADATA_TABLE as string, + IndexName: 'fileType-kbIngestionStatus-index', // Specify the GSI name + KeyConditionExpression: '#fileType = :processed and #kbIngestionStatus = :pending', + FilterExpression: '#status = :active and #fileFormat = :PDF', + ExpressionAttributeNames: { + '#fileType': 'fileType', + '#kbIngestionStatus': 'kbIngestionStatus', + '#status': 'status', + '#fileFormat': 'fileFormat', + }, + ExpressionAttributeValues: { + ':processed': 'processed', + ':pending': 'pending', + ':active': 'active', + ':PDF': 'PDF', // Match the FilterExpression placeholder + }, + }).promise(); + + // Query the DynamoDB GSI for deleted files + const deletedFilesResult = await dynamodbClient.query({ + TableName: FILE_METADATA_TABLE as string, + IndexName: 'fileType-kbIngestionStatus-index', + KeyConditionExpression: '#fileType = :processed and #kbIngestionStatus = :complete', + FilterExpression: '#status = :deleted', + ExpressionAttributeNames: { + '#fileType': 'fileType', + '#kbIngestionStatus': 'kbIngestionStatus', + '#status': 'status', + }, + ExpressionAttributeValues: { + ':processed': 'processed', + ':complete': 'complete', + ':deleted': 'deleted', + }, + }).promise(); + + // Determine if there are any new files added or existing files deleted + const modificationsExist = (newFilesResult.Items && newFilesResult.Items.length > 0) || + (deletedFilesResult.Items && deletedFilesResult.Items.length > 0); + + // Return true if any modifications (new additions or deletions) are found + return { + fileModificationsExist: modificationsExist, + }; + } catch (error) { + console.error('Error checking for file modifications for KB ingestion:', error); + throw error; + } +}; diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/copy-files.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/copy-files.ts new file mode 100644 index 00000000..efb6254e --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/copy-files.ts @@ -0,0 +1,144 @@ +import { S3, DynamoDB } from 'aws-sdk'; + +const s3Client = new S3(); +const dynamodbClient = new DynamoDB.DocumentClient({ region: 'us-east-1' }); + +export const handler = async (event: any): Promise => { + console.log("Event: ", event); + + const rawS3BucketQA = process.env.RAW_S3_QA as string; + const rawS3BucketProd = process.env.RAW_S3_PROD as string; + const tableName = process.env.FILE_METADATA_TABLE_NAME as string; + + console.log('Raw S3 Bucket (QA):', rawS3BucketQA); + console.log('Raw S3 Bucket (Prod):', rawS3BucketProd); + console.log('File Metadata Table:', tableName); + + if (!rawS3BucketQA || !rawS3BucketProd || !tableName) { + console.error('Required environment variables are missing.'); + return { + statusCode: 400, + body: JSON.stringify('Required environment variables are missing.'), + }; + } + + try { + // Step 1: Query DynamoDB for files with ragEvaluationStatus = 'PASSED' + const passedFiles = await getPassedFilesFromDynamoDB(tableName); + console.log(`Found ${passedFiles.length} files with RAG status 'PASSED'.`); + + const passedFileSet = new Set(passedFiles.map(file => file.fileId.split('/').pop())); + + // Step 2: List objects in the QA bucket + const listObjectsResponseQA = await s3Client + .listObjectsV2({ Bucket: rawS3BucketQA }) + .promise(); + + // Step 3: List objects in the Prod bucket + const listObjectsResponseProd = await s3Client + .listObjectsV2({ Bucket: rawS3BucketProd }) + .promise(); + + const prodObjectsMap = new Map(); + if (listObjectsResponseProd.Contents) { + for (const object of listObjectsResponseProd.Contents) { + if (object.Key && object.ETag) { + prodObjectsMap.set(object.Key, object.ETag); + } + } + } + + // If QA bucket is empty, delete all files from Prod bucket + if (!listObjectsResponseQA.Contents || listObjectsResponseQA.Contents.length === 0) { + console.log('QA bucket is empty. Deleting all objects from Prod bucket...'); + await deleteAllObjectsFromProd(listObjectsResponseProd.Contents || []); + return { + statusCode: 200, + body: JSON.stringify('QA bucket is empty. All objects deleted from Prod bucket.'), + }; + } + + // Create a set of all file names in the QA bucket + const qaFileSet = new Set( + listObjectsResponseQA.Contents.map(object => object.Key!) + ); + + // Step 4: Copy only the 'PASSED' files from QA to Prod if needed + for (const object of listObjectsResponseQA.Contents) { + if (object.Key && object.ETag && passedFileSet.has(object.Key)) { + const prodETag = prodObjectsMap.get(object.Key); + + if (!prodETag || prodETag !== object.ETag) { + const copyObjectParams: S3.CopyObjectRequest = { + Bucket: rawS3BucketProd, + CopySource: `${rawS3BucketQA}/${object.Key}`, + Key: object.Key, + }; + + console.log(`Copying object ${object.Key} from QA to Prod...`); + await s3Client.copyObject(copyObjectParams).promise(); + console.log(`Object ${object.Key} copied successfully.`); + } else { + console.log(`Object ${object.Key} already exists in Prod with the same content. Skipping...`); + } + } + } + + // Step 5: Delete files from Prod that are not in QA bucket + for (const object of listObjectsResponseProd.Contents || []) { + if (object.Key && !qaFileSet.has(object.Key)) { + const deleteObjectParams: S3.DeleteObjectRequest = { + Bucket: rawS3BucketProd, + Key: object.Key, + }; + + console.log(`Deleting object ${object.Key} from Prod bucket...`); + await s3Client.deleteObject(deleteObjectParams).promise(); + console.log(`Object ${object.Key} deleted successfully from Prod.`); + } + } + + return { + statusCode: 200, + body: JSON.stringify('Objects synchronized successfully between QA and Prod.'), + }; + } catch (error) { + console.error('Error synchronizing objects:', error); + return { + statusCode: 500, + body: JSON.stringify('Error synchronizing objects.'), + }; + } +}; + +// Function to query DynamoDB for files with ragEvaluationStatus = 'PASSED' +async function getPassedFilesFromDynamoDB(tableName: string): Promise { + const params = { + TableName: tableName, + FilterExpression: 'ragEvaluationStatus = :status', + ExpressionAttributeValues: { + ':status': 'PASSED', + }, + }; + + const result = await dynamodbClient.scan(params).promise(); + return result.Items || []; +} + +// Function to delete all objects from the Prod bucket +async function deleteAllObjectsFromProd(objects: S3.ObjectList) { + const deleteParams: S3.DeleteObjectsRequest = { + Bucket: process.env.RAW_S3_PROD as string, + Delete: { + Objects: objects.map(object => ({ Key: object.Key! })), + }, + }; + + if (deleteParams.Delete.Objects.length > 0) { + console.log('Deleting all objects from Prod bucket...'); + await s3Client.deleteObjects(deleteParams).promise(); + console.log('All objects deleted from Prod bucket.'); + } else { + console.log('No objects to delete from Prod bucket.'); + } +} \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/delete-stacks.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/delete-stacks.ts new file mode 100644 index 00000000..45f93cfb --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/delete-stacks.ts @@ -0,0 +1,160 @@ +import { CloudFormation, S3 } from 'aws-sdk'; + +const regions = ['us-east-1', 'us-west-2']; // TODO: Remove hardcoding + +export const handler = async (event: any) => { + console.log('Event:', event); + + // Perform the deletion in both regions + const deletePromises = regions.map(region => deleteResourcesInRegion(region)); + + // Wait for all deletions across both regions to complete + await Promise.all(deletePromises); + console.log('All delete requests across regions sent.'); +}; + +// Function to delete stacks and S3 buckets in a specific region +const deleteResourcesInRegion = async (region: string) => { + console.log(`Processing region: ${region}`); + + // Create CloudFormation and S3 clients for the specified region + const cloudFormationClient = new CloudFormation({ region }); + const s3Client = new S3({ region }); + + // Delete stacks + await deleteStacksInRegion(cloudFormationClient, region); + + // Delete S3 buckets + await deleteS3BucketsInRegion(s3Client, region); +}; + +// Function to delete stacks in a specific region +const deleteStacksInRegion = async (cloudFormationClient: CloudFormation, region: string) => { + console.log(`Deleting stacks in region: ${region}`); + + const stacks = await cloudFormationClient.listStacks().promise(); + console.log(`Stacks in ${region}:`, stacks); + + // Filter stacks that start with 'QA-' or 'Prod-' and are not already deleted + const appStacks = stacks.StackSummaries?.filter(stack => + (stack.StackName?.startsWith('QA') || stack.StackName?.startsWith('Prod')) || stack.StackName?.startsWith('PostQA') || stack.StackName?.startsWith('PreQA') || stack.StackName?.startsWith('PreProd') + && stack.StackStatus !== 'DELETE_COMPLETE' + ); + + console.log(`App Stacks in ${region}:`, appStacks); + + // Trigger deletion for all matching stacks in parallel in this region + const deletePromises = appStacks!.map(stack => { + console.log(`Initiating deletion for stack: ${stack.StackName} in region ${region}`); + return deleteStackWithRetry(cloudFormationClient, stack.StackName!); + }); + + // Wait for all deletion operations to be initiated + await Promise.all(deletePromises); + console.log(`All delete requests sent for stacks in region: ${region}`); +}; + +// Function to delete S3 buckets in a specific region +const deleteS3BucketsInRegion = async (s3Client: S3, region: string) => { + console.log(`Deleting S3 buckets in region: ${region}`); + + const buckets = await s3Client.listBuckets().promise(); + console.log(`Buckets:`, buckets); + + // Filter buckets that start with 'codepipelinestack' + const targetBuckets = buckets.Buckets?.filter(bucket => + bucket.Name?.startsWith('codepipelinestack') + ); + + console.log(`Target Buckets in ${region}:`, targetBuckets); + + // Trigger deletion for all matching buckets in parallel in this region + const deletePromises = targetBuckets!.map(bucket => { + console.log(`Initiating deletion for bucket: ${bucket.Name} in region ${region}`); + return deleteBucketWithRetry(s3Client, bucket.Name!); + }); + + // Wait for all deletion operations to be initiated + await Promise.all(deletePromises); + console.log(`All delete requests sent for buckets in region: ${region}`); +}; + +// Retry mechanism for deleting a stack +const deleteStackWithRetry = async ( + cloudFormationClient: CloudFormation, + stackName: string, + retries: number = 5, + delay: number = 10000 +) => { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + await cloudFormationClient.deleteStack({ StackName: stackName }).promise(); + console.log(`Delete request successfully sent for stack: ${stackName}`); + + // Wait for the stack to be deleted + await waitForStackDeletion(cloudFormationClient, stackName); + return; // Exit the loop if successful + } catch (error) { + console.error(`Attempt ${attempt} to delete stack ${stackName} failed:`, error); + if (attempt < retries) { + console.log(`Retrying in ${delay / 1000} seconds...`); + await new Promise(resolve => setTimeout(resolve, delay)); // Wait before retrying + } else { + console.error(`Failed to delete stack ${stackName} after ${retries} attempts.`); + } + } + } +}; + +// Function to wait for stack deletion +const waitForStackDeletion = async (cloudFormationClient: CloudFormation, stackName: string) => { + while (true) { + const stackStatus = await cloudFormationClient.describeStacks({ StackName: stackName }).promise(); + const status = stackStatus.Stacks?.[0].StackStatus; + + if (status === 'DELETE_COMPLETE') { + console.log(`Stack ${stackName} successfully deleted.`); + return; + } else if (status === 'DELETE_FAILED') { + throw new Error(`Failed to delete stack ${stackName}.`); + } else { + console.log(`Waiting for stack ${stackName} to be deleted. Current status: ${status}`); + await new Promise(resolve => setTimeout(resolve, 10000)); // Wait before checking again + } + } +}; + +// Retry mechanism for deleting a bucket +const deleteBucketWithRetry = async ( + s3Client: S3, + bucketName: string, + retries: number = 5, + delay: number = 10000 +) => { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + // Empty the bucket before deleting + const objects = await s3Client.listObjectsV2({ Bucket: bucketName }).promise(); + if (objects.Contents && objects.Contents.length > 0) { + const deleteParams = { + Bucket: bucketName, + Delete: { Objects: objects.Contents.map(obj => ({ Key: obj.Key! })) } + }; + await s3Client.deleteObjects(deleteParams).promise(); + console.log(`Deleted objects in bucket: ${bucketName}`); + } + + await s3Client.deleteBucket({ Bucket: bucketName }).promise(); + console.log(`Delete request successfully sent for bucket: ${bucketName}`); + return; // Exit the loop if successful + } catch (error) { + console.error(`Attempt ${attempt} to delete bucket ${bucketName} failed:`, error); + if (attempt < retries) { + console.log(`Retrying in ${delay / 1000} seconds...`); + await new Promise(resolve => setTimeout(resolve, delay)); // Wait before retrying + } else { + console.error(`Failed to delete bucket ${bucketName} after ${retries} attempts.`); + } + } + } +}; \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/evaluate-new-data-ingestion.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/evaluate-new-data-ingestion.ts new file mode 100644 index 00000000..8ffdd216 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/evaluate-new-data-ingestion.ts @@ -0,0 +1,86 @@ +import { DynamoDB } from 'aws-sdk'; +const dynamodbClient = new DynamoDB.DocumentClient(); +const tableName = process.env.FILE_METADATA_TABLE_NAME!; + +export const handler = async (): Promise => { + console.log('Evaluate Batch RAG Ingestion Lambda!'); + + // Placeholder for ingestion result (simulating a success rate of 85%) + const ingestionResult = { successRate: 85 }; + console.log('Ingestion Result:', JSON.stringify(ingestionResult, null, 2)); + + const threshold = 80; // Threshold for passing evaluation + + // Determine the RAG evaluation status + const ragStatus = ingestionResult.successRate >= threshold ? 'PASSED' : 'FAILED'; + const evaluationTimestamp = new Date().toISOString(); + + try { + // Step 1: Fetch all files with 'pending' RAG evaluation status + const pendingFiles = await getPendingRAGEvaluations(); + console.log(`Found ${pendingFiles.length} files with pending RAG evaluation.`); + + // Step 2: Update all pending files with the batch evaluation result + const updatePromises = pendingFiles.map((file) => { + const { fileId, fileType } = file; + console.log(`Updating ${fileId} with RAG status: ${ragStatus}`); + return updateRAGEvaluation(fileId, fileType, ragStatus, evaluationTimestamp); + }); + + // Step 3: Wait for all updates to complete + await Promise.all(updatePromises); + console.log('All pending evaluations processed successfully.'); + + return { + success: true, + message: `Batch RAG evaluation completed with status: ${ragStatus}.`, + }; + } catch (error) { + console.error('Error processing batch RAG evaluation:', error); + return { + success: false, + message: 'Failed to process batch RAG evaluation.', + }; + } +}; + +// Function to fetch all files with 'pending' RAG evaluation status +async function getPendingRAGEvaluations(): Promise { + const params = { + TableName: tableName, + FilterExpression: 'ragEvaluationStatus = :pending', + ExpressionAttributeValues: { ':pending': 'pending' }, + }; + + const result = await dynamodbClient.scan(params).promise(); + return result.Items || []; +} + +// Function to update RAG evaluation status and timestamp in DynamoDB +async function updateRAGEvaluation( + fileId: string, + fileType: string, + ragStatus: string, + evaluationTimestamp: string +): Promise { + try { + await dynamodbClient.update({ + TableName: tableName, + Key: { + fileId: fileId, // Partition key + fileType: fileType, // Sort key + }, + UpdateExpression: 'set ragEvaluationStatus = :ragStatus, ragEvaluationTimestamp = :timestamp', + ExpressionAttributeValues: { + ':ragStatus': ragStatus, + ':timestamp': evaluationTimestamp, + }, + }).promise(); + + console.log(`Updated RAG evaluation to '${ragStatus}' for fileId: ${fileId}`); + } catch (error) { + console.error(`Error updating RAG evaluation for ${fileId}:`, error); + throw error; + } +} + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/file-upload-processor.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/file-upload-processor.ts new file mode 100644 index 00000000..b6bab223 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/file-upload-processor.ts @@ -0,0 +1,102 @@ +import { S3 } from 'aws-sdk'; + +const s3Client = new S3(); + + +const handler = async (event: any): Promise => { + console.log('Event: ', event); + + + // Access the environment variables + const rawS3BucketARN = process.env.RAW_S3 as string; + const processedS3BucketARN = process.env.PROCESSED_S3 as string; + + console.log('Raw S3 Bucket ARN: ', rawS3BucketARN); + console.log('Processed S3 Bucket ARN: ', processedS3BucketARN); + + if (!rawS3BucketARN || !processedS3BucketARN) { + console.error('Environment variables RAW_S3 and PROCESSED_S3 must be defined'); + return { + statusCode: 400, + body: JSON.stringify('Environment variables are not set.'), + }; + } + + // Check if event.Records is defined and has at least one record + if (!event.Records || event.Records.length === 0) { + console.error('No records found in the event.'); + return { + statusCode: 400, + body: JSON.stringify('No records found in the event.'), + }; + } + + // Extract bucket names from ARNs + const rawS3Bucket = rawS3BucketARN.split(':').pop()!; + const processedS3Bucket = processedS3BucketARN.split(':').pop()!; + console.log('Raw S3 Bucket: ', rawS3Bucket); + console.log('Processed S3 Bucket: ', processedS3Bucket); + + + // Extract the file name and event type from the SQS message + const record = event.Records[0]; + const messageBody = JSON.parse(record.body); + console.log('Message Body: ', messageBody); + const { fileName, eventType } = messageBody; + + // Check if the eventType is 'Object Created' + if (eventType === 'Object Created') { + console.log(`Processing file: ${fileName}`); + // Copy the file from the raw S3 bucket to the processed S3 bucket + console.log(`Copying file ${fileName} from ${rawS3Bucket} to ${processedS3Bucket}`); + + try { + await s3Client.copyObject({ + Bucket: processedS3Bucket, + CopySource: `${rawS3Bucket}/${fileName}`, + Key: fileName, + }).promise(); + + console.log(`File ${fileName} copied successfully to ${processedS3Bucket}`); + } catch (error) { + console.error(`Error copying file ${fileName} to ${processedS3Bucket}: ${error}`); + return { + statusCode: 500, + body: JSON.stringify(`Error copying file ${fileName} to ${processedS3Bucket}: ${error}`), + }; + } + } else if (eventType === 'Object Deleted') { + console.log(`Deleting file: ${fileName}`); + // Delete the file from the processed S3 bucket + console.log(`Deleting file ${fileName} from ${processedS3Bucket}`); + + try { + await s3Client.deleteObject({ + Bucket: processedS3Bucket, + Key: fileName, + }).promise(); + + console.log(`File ${fileName} deleted successfully from ${processedS3Bucket}`); + } catch (error) { + console.error(`Error deleting file ${fileName} from ${processedS3Bucket}: ${error}`); + return { + statusCode: 500, + body: JSON.stringify(`Error deleting file ${fileName} from ${processedS3Bucket}: ${error}`), + }; + } + + } else { + console.log(`Event type ${eventType} is not supported in file upload processor lambda.`); + return { + statusCode: 400, + body: JSON.stringify(`Event type ${eventType} is not supported in file upload processor lambda.`), + }; + } + + return { + statusCode: 200, + body: JSON.stringify("File upload processor Lambda executed successfully!"), + }; +} + +export { handler } \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/get-ingestion-job-status.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/get-ingestion-job-status.ts new file mode 100644 index 00000000..6794c214 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/get-ingestion-job-status.ts @@ -0,0 +1,27 @@ + +import { BedrockAgentClient, GetIngestionJobCommand } from "@aws-sdk/client-bedrock-agent"; + +const bedrockClient = new BedrockAgentClient({ region: process.env.AWS_REGION }); + +export const handler = async (event: any): Promise => { + const { KnowledgeBaseId, DataSourceId, IngestionJobId } = event; + + const command = new GetIngestionJobCommand({ + knowledgeBaseId: KnowledgeBaseId, + dataSourceId: DataSourceId, + ingestionJobId: IngestionJobId, + }); + + try { + const response = await bedrockClient.send(command); + const job = response.ingestionJob; + console.log('Ingestion Job status:', job?.status); + return { + status: job?.status, + ingestionJob: job, + }; + } catch (error) { + console.error('Error getting ingestion job status:', error); + throw error; + } +}; diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/kb-data-ingestion.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/kb-data-ingestion.ts new file mode 100644 index 00000000..c2d26bee --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/kb-data-ingestion.ts @@ -0,0 +1,125 @@ +import { DynamoDB } from "aws-sdk"; +import { BedrockAgentClient, StartIngestionJobCommand, GetIngestionJobCommand } from "@aws-sdk/client-bedrock-agent"; + +const dynamodbClient = new DynamoDB.DocumentClient(); +const bedrockClient = new BedrockAgentClient({ region: process.env.AWS_REGION }); + +// const DYNAMODB_TABLE = process.env.PROCESSED_FILES_TABLE; +// const STAGE_NAME = process.env.STAGE_NAME; +const FILE_METADATA_TABLE = process.env.FILE_METADATA_TABLE; + +// if (!FILE_METADATA_TABLE_NAME) { +// throw new Error('DYNAMODB_TABLE environment variable is not set'); +// } + +export const handler = async (event: any, context: any): Promise => { + // console.log(`Hello KB Data Ingestion Lambda for ${STAGE_NAME} environment!`); + console.log('Event:', event); + console.log('From kb-data-ingestion Lambda'); + + const kbS3ProcessedDataSource = process.env.PROCESSED_S3; + const knowledgeBaseId = process.env.KNOWLEDGE_BASE_ID; + const dataSourceId = process.env.DATA_SOURCE_ID; + + if (!kbS3ProcessedDataSource || !knowledgeBaseId || !dataSourceId) { + throw new Error('Environment variables KB_S3_PROCESSED_DATA_SOURCE, KNOWLEDGE_BASE_ID, and DATA_SOURCE_ID must be defined'); + } + + console.log('KB S3 Processed Data Source: ', kbS3ProcessedDataSource); + console.log('Knowledge Base ID: ', knowledgeBaseId); + console.log('Data Source ID: ', dataSourceId); + + // const newFilesAvailable = await areNewFilesAvailableForKbIngestion(); + // if (!newFilesAvailable) { + // console.log('No new files detected. Skipping data ingestion.'); + // return { + // // dataingestionStatus: 'No new files to ingest', + // newFilesAvailable: false, + // }; + // } + + // console.log('New files detected. Proceeding with data ingestion...'); + console.log('Starting data ingestion job...'); + const jobInfo = await startIngestionJob(knowledgeBaseId, dataSourceId, context.awsRequestId); + + // Return job information + return { + // dataingestionStatus: 'Started data ingestion job successfully', + // newFilesAvailable: true, + // fileModificationsExist: true, + JobId: jobInfo.JobId, + KnowledgeBaseId: knowledgeBaseId, + DataSourceId: dataSourceId, + }; +}; + +const startIngestionJob = async (knowledgeBaseId: string, dataSourceId: string, clientToken: string): Promise<{ JobId: string }> => { + const input = { + knowledgeBaseId: knowledgeBaseId, + dataSourceId: dataSourceId, + clientToken: clientToken, + }; + const command = new StartIngestionJobCommand(input); + + const startJobResponse = await bedrockClient.send(command); + const job = startJobResponse.ingestionJob; + console.log('Ingestion Job started:', job); + + return { + JobId: job?.ingestionJobId as string, + }; +}; + +// // Function to check if new files are marked in DynamoDB +// const checkForNewFiles = async (): Promise => { +// const params = { +// TableName: DYNAMODB_TABLE as string, +// Key: { fileName: 'newFilesFlag', environment: STAGE_NAME }, +// }; +// console.log(`Checking DynamoDB for new files in ${STAGE_NAME} environment...`); + +// const data = await dynamodbClient.get(params).promise(); +// console.log(`DynamoDB response for checkForNewFiles in ${STAGE_NAME} environment:`, data); + +// if (data.Item && data.Item.hasNewFiles) { +// console.log('New files found in DynamoDB.'); +// return true; +// } + +// console.log('No new files found in DynamoDB.'); +// return false; +// }; + + +async function areNewFilesAvailableForKbIngestion(): Promise { + try { + // Query the DynamoDB GSI for processed files with pending KB ingestion status and active status + const result = await dynamodbClient.query({ + TableName: FILE_METADATA_TABLE as string, + IndexName: 'fileType-kbIngestionStatus-index', // Specify the GSI name + KeyConditionExpression: '#fileType = :processed and #kbIngestionStatus = :pending', + FilterExpression: '#status = :active and #fileFormat = :PDF', + ExpressionAttributeNames: { + '#fileType': 'fileType', + '#kbIngestionStatus': 'kbIngestionStatus', + '#status': 'status', + '#fileFormat': 'fileFormat', + }, + ExpressionAttributeValues: { + ':processed': 'processed', + ':pending': 'pending', + ':active': 'active', + ':PDF': 'PDF', // Match the FilterExpression placeholder + }, + }).promise(); + + // Return true if there are any matching items, otherwise false + return result.Items !== undefined && result.Items.length > 0; + } catch (error) { + console.error('Error checking for new files for KB ingestion:', error); + throw error; + } +} + + + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/metadata-tracking.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/metadata-tracking.ts new file mode 100644 index 00000000..cc029192 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/metadata-tracking.ts @@ -0,0 +1,159 @@ +import { S3Event, S3Handler } from 'aws-lambda'; +import { DynamoDB, S3 } from 'aws-sdk'; + +const dynamodbClient = new DynamoDB.DocumentClient(); +const s3Client = new S3(); +const tableName = process.env.FILE_METADATA_TABLE_NAME!; + + +export const handler: S3Handler = async (event: S3Event) => { + console.log('Received S3 event:', JSON.stringify(event, null, 2)); + + try { + for (const record of event.Records) { + const bucketName = record.s3.bucket.name; + const objectKey = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' ')); + const eventType = record.eventName; + console.log(`Processing S3 event: ${eventType} for ${objectKey} in bucket ${bucketName}`); + + if (eventType.startsWith('ObjectRemoved')) { + console.log(`Received ObjectRemoved event for ${objectKey} in bucket ${bucketName}`); + // Update the status of the file to 'deleted' in DynamoDB + await updateFileStatus(bucketName, objectKey, 'deleted'); + } else if (eventType.startsWith('ObjectCreated')) { + + const fileSize = record.s3.object.size; + + // Determine fileType based on the bucket name + const fileType = bucketName.includes('raw-data-source') ? 'raw' : 'processed'; + console.log(`Checking ${fileType} file: ${objectKey} from bucket: ${bucketName}`); + + // Extract file format based on the file extension + const fileFormat = getFileFormat(objectKey); + console.log(`File format: ${fileFormat}`); + + // Check if the associated metadata file exists in S3 + const metadataFileKey = `${objectKey}.metadata.json`; + const metadataExists = await checkMetadataFileExists(bucketName, metadataFileKey); + + // Determine the kbIngestionStatus based on the fileType + const kbIngestionStatus = fileType === 'raw' ? 'not applicable' : 'pending'; + + // Construct metadata to be saved in DynamoDB + const fileMetadata = { + fileId: `${bucketName}/${objectKey}`, // Unique file identifier + fileType: fileType, // 'raw' or 'processed' + bucketName: bucketName, + fileName: objectKey, + fileSize: fileSize, + fileFormat: fileFormat, // File format (e.g., PDF, PPTX) + metadataFileExists: metadataExists, // Boolean indicating if associated metadata.json exists + lastModified: new Date().toISOString(), + status: 'active', // Status of the file (active, deleted), set the initial status to 'active'; when a new file is added, the status is set to 'active' + kbIngestionStatus: kbIngestionStatus, // Set based on fileType: 'not applicable' for raw, 'pending' for processed + ragEvaluationStatus: 'pending', // Status of the RAG evaluation (pending, passed, failed) + ragEvaluationTimestamp: '', // Timestamp when RAG evaluation was performed + + }; + + // Save the metadata in DynamoDB + await dynamodbClient.put({ + TableName: tableName, + Item: fileMetadata, + }).promise(); + } + } + } catch (error) { + console.error('Error processing S3 event:', error); + throw error; + } +}; + +// Extracts file format from the file extension in the object key +function getFileFormat(objectKey: string): string { + const extension = objectKey.split('.').pop() || ''; + switch (extension.toLowerCase()) { + case 'pdf': + return 'PDF'; + case 'ppt': + case 'pptx': + return 'PowerPoint'; + case 'doc': + case 'docx': + return 'Word Document'; + case 'xls': + case 'xlsx': + return 'Excel Spreadsheet'; + case 'csv': + return 'CSV'; + case 'txt': + return 'Text File'; + // Add more cases for other file formats as needed + default: + return 'Unknown'; + } +} + +// Checks if the associated metadata file exists in the S3 bucket +async function checkMetadataFileExists(bucketName: string, metadataFileKey: string): Promise { + try { + // Use headObject to check if the metadata file exists + await s3Client.headObject({ Bucket: bucketName, Key: metadataFileKey }).promise(); + return true; // Metadata file exists + } catch (error) { + if ((error as any).code === 'NotFound') { + return false; // Metadata file does not exist + } + console.error('Error checking metadata file:', error); + throw error; + } +} + + +// Updates the status of the file in DynamoDB +async function updateFileStatus(bucketName: string, objectKey: string, status: string): Promise { + try { + // Determine fileType based on the bucket name + const fileType = bucketName.includes('raw-data-source') ? 'raw' : 'processed'; + + // Given your DynamoDB table schema, it uses a composite primary key consisting of a partition key fileId and a sort key fileType. Therefore, when you're performing any update, get, or delete operations on the items in this table, you must include both the fileId and fileType as part of the key. + // Update DynamoDB with both partition key and sort key + await dynamodbClient.update({ + TableName: tableName, + Key: { + fileId: `${bucketName}/${objectKey}`, // Partition key + fileType: fileType, // Sort key + }, + UpdateExpression: 'set #s = :status', + ExpressionAttributeNames: { '#s': 'status' }, + ExpressionAttributeValues: { ':status': status }, + }).promise(); + console.log(`Updated status to '${status}' for fileId: ${bucketName}/${objectKey}, fileType: ${fileType}`); + } catch (error) { + console.error('Error updating file status:', error); + throw error; + } +} + + +// Updates the ingestionStatus of the file in DynamoDB +async function updateIngestionStatus(bucketName: string, objectKey: string, status: string): Promise { + const fileType = bucketName.includes('raw-data-source') ? 'raw' : 'processed'; + + try { + await dynamodbClient.update({ + TableName: tableName, + Key: { + fileId: `${bucketName}/${objectKey}`, // Partition key + fileType: fileType, // Sort key + }, + UpdateExpression: 'set ingestionStatus = :status', + ExpressionAttributeValues: { ':status': status }, + }).promise(); + console.log(`Updated ingestionStatus to '${status}' for fileId: ${bucketName}/${objectKey}, fileType: ${fileType}`); + } catch (error) { + console.error('Error updating ingestion status:', error); + throw error; + } +} + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/package-lambda.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/package-lambda.ts new file mode 100644 index 00000000..d71ff876 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/package-lambda.ts @@ -0,0 +1,56 @@ +import { join } from 'path'; +import { existsSync, mkdirSync, rmdirSync, copyFileSync, createWriteStream } from 'fs'; +import * as child_process from 'child_process'; +import archiver from 'archiver'; + +async function packageLambda() { + const tempDir = join(__dirname, 'tmp', 'custom_chunking_lambda_package'); + // const lambdaCodePath = join(__dirname, 'src', 'services', 'CustomChunker', 'custom_chunking_python.py'); + const lambdaCodePath = join(__dirname, 'CustomChunker', 'custom_chunking_lambda_function.py'); + const zipFilePath = join(__dirname, 'lambda_package.zip'); + + // Clean up the temp directory if it exists + if (existsSync(tempDir)) { + console.log(`Removing existing temporary directory: ${tempDir}`); + rmdirSync(tempDir, { recursive: true }); + } + + // Create the temp directory + console.log(`Creating temporary directory: ${tempDir}`); + mkdirSync(tempDir, { recursive: true }); + + // Copy the Lambda code to the temp directory + console.log(`Copying Lambda code from ${lambdaCodePath} to ${tempDir}`); + copyFileSync(lambdaCodePath, join(tempDir, 'custom_chunking_lambda_function.py')); + + // Install the required dependencies (e.g., pypdf) into the temp directory + console.log('Installing dependencies in the temp directory...'); + child_process.execSync('pip install pypdf --target ' + tempDir, { stdio: 'inherit' }); + + // Create the zip file for the Lambda package + console.log(`Creating zip file: ${zipFilePath}`); + const output = createWriteStream(zipFilePath); + const archive = archiver('zip', { zlib: { level: 9 } }); + + archive.pipe(output); + archive.directory(tempDir, false); + + // Finalize the archive and return the promise + return new Promise((resolve, reject) => { + archive.finalize() + .then(() => { + console.log(`Lambda package zip finalized at: ${zipFilePath}`); + resolve(); + }) + .catch((err) => { + console.error('Error while creating Lambda package zip:', err); + reject(err); + }); + }); +} + +// Run the packaging script +packageLambda().catch((err) => { + console.error('Packaging failed:', err); +}); + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/s3-event-processor.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/s3-event-processor.ts new file mode 100644 index 00000000..be5f4dc6 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/s3-event-processor.ts @@ -0,0 +1,21 @@ + +const handler = async (event: any): Promise => { + console.log('Event: ', event); + + // Extract the file name and event type + const fileName = event.detail.object.key; + const eventType = event['detail-type']; + + // Create a new JSON output + const typeOfS3Event = { + fileName: fileName, + eventType: eventType + }; + + console.log('S3 Event Processor Lambda Output: ', typeOfS3Event); + + // Return the response directly without wrapping it in a Payload object + return typeOfS3Event; +} + +export { handler } \ No newline at end of file diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/start-move-files-state-machine.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/start-move-files-state-machine.ts new file mode 100644 index 00000000..48e62507 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/start-move-files-state-machine.ts @@ -0,0 +1,39 @@ +import { SSM, StepFunctions } from 'aws-sdk'; +import { Handler } from 'aws-lambda'; + +const ssm = new SSM({ region: 'us-west-2' }); +const stepfunctions = new StepFunctions({ region: 'us-west-2' }); + +export const handler: Handler = async (event) => { + try { + // Get the state machine ARN from SSM Parameter Store + const param = await ssm.getParameter({ + Name: `/${process.env.PIPELINE_NAME}/Prod/move-files-state-machine-arn`, + }).promise(); + + const stateMachineArn = param.Parameter?.Value; + + if (!stateMachineArn) { + throw new Error('State Machine ARN not found in SSM'); + } + + // Start the Step Function execution + const result = await stepfunctions.startExecution({ + stateMachineArn, + input: JSON.stringify(event), + }).promise(); + + console.log('Step Function Execution started:', result); + + return { + statusCode: 200, + body: 'Step Function execution started successfully', + }; + } catch (error) { + console.error('Error starting Step Function:', error); + return { + statusCode: 500, + body: 'Failed to start Step Function', + }; + } +}; diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/trigger-approval.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/trigger-approval.ts new file mode 100644 index 00000000..e5ae7015 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/trigger-approval.ts @@ -0,0 +1,161 @@ +import { CodePipelineClient, GetPipelineStateCommand, PutApprovalResultCommand } from "@aws-sdk/client-codepipeline"; +import { Handler } from "aws-lambda"; + +// Initialize the CodePipeline client +const codePipelineClient = new CodePipelineClient({ region: process.env.AWS_REGION }); + +export const handler: Handler = async (event: any) => { + console.log("Event received to approve pipeline: ", event); + + try { + // Extract necessary environment variables + const pipelineName = process.env.PIPELINE_NAME; + const stageName = "PostQAApproval"; // Now the manual approval is in the PostQAApproval stage + const actionName = "ManualApprovalForProduction"; // Name of the manual approval action + const evaluationResult = event.success; // Get the evaluation result + + if (!pipelineName) { + throw new Error("Pipeline name is not defined"); + } + + // Get the current state of the pipeline + const getPipelineStateCommand = new GetPipelineStateCommand({ + name: pipelineName, + }); + + const pipelineStateResponse = await codePipelineClient.send(getPipelineStateCommand); + console.log("Pipeline state response: ", pipelineStateResponse); + + // Check the latest pipeline version and timestamp + const pipelineVersion = pipelineStateResponse.pipelineVersion; + const lastUpdated = pipelineStateResponse.updated; + console.log(`Pipeline Version: ${pipelineVersion}, Last Updated: ${lastUpdated}`); + + // Retrieve the stages from the pipeline + const stages = pipelineStateResponse.stageStates; + if (!stages) { + throw new Error("Pipeline stages are not available."); + } + + // Find the pipelineExecutionId from the PostQAApproval stage (ensure it's the latest one) + let postQAApprovalExecutionId: string | undefined; + const postQAStageName = "PostQAApproval"; // Ensure this matches the exact stage name for PostQAApproval + + for (const stage of stages) { + if (stage.stageName === postQAStageName && stage.latestExecution) { + postQAApprovalExecutionId = stage.latestExecution.pipelineExecutionId; + console.log(`PostQAApproval Stage Execution ID: ${postQAApprovalExecutionId}`); + break; + } + } + + if (!postQAApprovalExecutionId) { + throw new Error("PostQAApproval execution ID not found."); + } + + // Check for previous approval status + let approvalAlreadySucceeded = false; + let approvalToken: string | undefined; + + for (const stage of stages) { + if (stage.stageName === stageName) { + console.log(`Processing PostQAApproval stage with execution ID: ${stage.latestExecution?.pipelineExecutionId}`); + + if (stage.actionStates) { + console.log("Stage action states: ", stage.actionStates); + for (const action of stage.actionStates) { + if (action.actionName === actionName) { + // Check if approval has already succeeded + if (action.latestExecution?.status === "Succeeded") { + approvalAlreadySucceeded = true; + console.log("Manual approval already succeeded. Resetting approval."); + } else if (action.latestExecution?.status === "InProgress") { + approvalToken = action.latestExecution.token; + console.log(`Found approval token: ${approvalToken}`); + } + break; + } + } + } + } + } + + // Reset manual approval if it was already marked as succeeded + if (approvalAlreadySucceeded) { + const resetApprovalCommand = new PutApprovalResultCommand({ + pipelineName: pipelineName, + stageName: stageName, + actionName: actionName, + result: { + summary: "Manual approval reset due to new changes.", + status: "Rejected", // Reject the previous approval to force re-triggering + }, + token: approvalToken, + }); + + await codePipelineClient.send(resetApprovalCommand); + console.log("Previous approval was reset."); + return { + statusCode: 200, + body: "Manual approval reset successfully. Waiting for new approval.", + }; + } + + if (!approvalToken) { + console.warn("Approval token not found for current execution. Trying fallback to the latest execution."); + + // Fallback mechanism to find any manual approval token in the PostQAApproval stage + for (const stage of stages) { + if (stage.stageName === stageName) { + if (stage.actionStates) { + console.log("Stage action states: ", stage.actionStates); + for (const action of stage.actionStates) { + if (action.actionName === actionName && action.latestExecution?.status === "InProgress") { + approvalToken = action.latestExecution.token; + console.log(`Fallback approval token found: ${approvalToken}`); + break; + } + } + } + } + } + + if (!approvalToken) { + throw new Error("Approval token not found or no manual approval in progress."); + } + } + + // Perform the approval if the evaluation succeeded + if (evaluationResult === true) { + const putApprovalResultCommand = new PutApprovalResultCommand({ + pipelineName: pipelineName, + stageName: stageName, + actionName: actionName, + result: { + summary: "Evaluation passed. Proceeding to production.", + status: "Approved", + }, + token: approvalToken, // Use the approval token retrieved + }); + + const response = await codePipelineClient.send(putApprovalResultCommand); + console.log(`Pipeline ${pipelineName} manual approval successfully completed.`); + return { + statusCode: 200, + body: "Manual approval approved, pipeline continues to production.", + }; + } else { + console.log(`Evaluation failed. Production deployment will not proceed.`); + return { + statusCode: 400, + body: "Evaluation failed. Pipeline not promoted to production.", + }; + } + } catch (error) { + console.error("Error during pipeline approval: ", error); + return { + statusCode: 500, + body: 'Failed to approve pipeline', + }; + } +}; diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/trigger-rag-evaluation.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/trigger-rag-evaluation.ts new file mode 100644 index 00000000..710417de --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/trigger-rag-evaluation.ts @@ -0,0 +1,45 @@ +import { StepFunctions, SSM } from 'aws-sdk'; +import { APIGatewayProxyHandler } from 'aws-lambda'; + +const stepfunctions = new StepFunctions(); +const ssm = new SSM(); + +export const handler: APIGatewayProxyHandler = async (event: any): Promise => { + // Define the parameter name that stores the RAG Evaluation State Machine ARN + const ragEvaluationSsmParameter = `/${process.env.CODE_PIPELINE_NAME}/PostQAApproval/rag-evaluation-state-machine-arn`; + + try { + // Fetch the RAG evaluation state machine ARN from SSM + const ssmResponse = await ssm.getParameter({ + Name: ragEvaluationSsmParameter, + // WithDecryption: true // If the parameter is encrypted + }).promise(); + + const ragEvaluationStateMachineArn = ssmResponse.Parameter?.Value; + + if (!ragEvaluationStateMachineArn) { + throw new Error('RAG Evaluation State Machine ARN not found in SSM'); + } + + // Prepare Step Functions parameters + const params = { + stateMachineArn: ragEvaluationStateMachineArn, // Use the fetched ARN from SSM + input: JSON.stringify(event), // Pass the input from the kbDataIngestionStateMachine + }; + + // Start the execution of the RAG evaluation state machine + const data = await stepfunctions.startExecution(params).promise(); + console.log('Successfully started RAG Evaluation State Machine:', data); + + return { + statusCode: 200, + body: JSON.stringify('RAG Evaluation triggered successfully'), + }; + } catch (error) { + console.error('Error triggering RAG Evaluation State Machine:', error); + return { + statusCode: 500, + body: JSON.stringify('Failed to start RAG Evaluation') + }; + } +}; diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/update-file-metadata.ts b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/update-file-metadata.ts new file mode 100644 index 00000000..e6367025 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/src/services/update-file-metadata.ts @@ -0,0 +1,100 @@ +import { DynamoDB } from 'aws-sdk'; + +const dynamodbClient = new DynamoDB.DocumentClient(); +const tableName = process.env.FILE_METADATA_TABLE!; + +export const handler = async (): Promise => { + try { + // Step 1: Query for new files added (pending ingestion) + const newFilesResult = await dynamodbClient.query({ + TableName: tableName, + IndexName: 'fileType-kbIngestionStatus-index', + KeyConditionExpression: '#fileType = :processed and #kbIngestionStatus = :pending', + FilterExpression: '#status = :active and #fileFormat = :PDF', + ExpressionAttributeNames: { + '#fileType': 'fileType', + '#kbIngestionStatus': 'kbIngestionStatus', + '#status': 'status', + '#fileFormat': 'fileFormat', + }, + ExpressionAttributeValues: { + ':processed': 'processed', + ':pending': 'pending', + ':active': 'active', + ':PDF': 'PDF', + }, + }).promise(); + + if (newFilesResult.Items && newFilesResult.Items.length > 0) { + console.log(`Found ${newFilesResult.Items.length} new items to update to 'complete'.`); + + // Step 2: Update the kbIngestionStatus to 'complete' for each matching item + for (const item of newFilesResult.Items) { + await updateKbIngestionStatus(item.fileId, item.fileType, 'complete'); + } + } else { + console.log('No new items found matching the filters.'); + } + + // Step 3: Query for deleted files that have kbIngestionStatus as 'complete' + const deletedFilesResult = await dynamodbClient.query({ + TableName: tableName, + IndexName: 'fileType-kbIngestionStatus-index', + KeyConditionExpression: '#fileType = :processed and #kbIngestionStatus = :complete', + FilterExpression: '#status = :deleted', + ExpressionAttributeNames: { + '#fileType': 'fileType', + '#kbIngestionStatus': 'kbIngestionStatus', + '#status': 'status', + }, + ExpressionAttributeValues: { + ':processed': 'processed', + ':complete': 'complete', + ':deleted': 'deleted', + }, + }).promise(); + + if (deletedFilesResult.Items && deletedFilesResult.Items.length > 0) { + console.log(`Found ${deletedFilesResult.Items.length} deleted items to update to 'removed'.`); + + // Step 4: Update the kbIngestionStatus to 'removed' for each matching item + for (const item of deletedFilesResult.Items) { + await updateKbIngestionStatus(item.fileId, item.fileType, 'removed'); + } + } else { + console.log('No deleted items found to update to "removed".'); + } + + // Return a success message as JSON string + return JSON.stringify({ + message: 'File metadata updated successfully', + }); + } catch (error) { + console.error('Error updating file metadata:', error); + throw error; + } +}; + +// Function to update the kbIngestionStatus to a specified status +async function updateKbIngestionStatus(fileId: string, fileType: string, status: string): Promise { + try { + await dynamodbClient.update({ + TableName: tableName, + Key: { + fileId: fileId, // Partition key + fileType: fileType, // Sort key + }, + UpdateExpression: 'set #kbIngestionStatus = :status', + ExpressionAttributeNames: { + '#kbIngestionStatus': 'kbIngestionStatus', + }, + ExpressionAttributeValues: { + ':status': status, + }, + }).promise(); + console.log(`Updated kbIngestionStatus to '${status}' for fileId: ${fileId}, fileType: ${fileType}`); + } catch (error) { + console.error(`Error updating kbIngestionStatus to '${status}':`, error); + throw error; + } +} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/__pycache__/typing_extensions.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/__pycache__/typing_extensions.cpython-38.pyc new file mode 100644 index 00000000..1ac814fc Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/__pycache__/typing_extensions.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/custom_chunking_python.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/custom_chunking_python.py new file mode 100644 index 00000000..aa27821a --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/custom_chunking_python.py @@ -0,0 +1,247 @@ + +import json +import logging +import boto3 +import base64 +from abc import ABC, abstractmethod +from typing import List +from urllib.parse import urlparse +from pypdf import PdfReader +from io import BytesIO +from botocore.exceptions import ClientError + +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +# Abstract class for chunking text +class Chunker(ABC): + @abstractmethod + def chunk(self, text: str) -> List[str]: + pass + +# Simple implementation of the Chunker that splits text into chunks of 10 words +class SimpleChunker(Chunker): + def chunk(self, text: str) -> List[str]: + words = text.split() + return [' '.join(words[i:i + 100]) for i in range(0, len(words), 100)] + +# Lambda function entry point +def lambda_handler(event, context): + logger.debug('Starting lambda_handler') + logger.debug(f'Input event: {json.dumps(event, indent=2)}') + + # Initialize the S3 and Bedrock clients + s3 = boto3.client('s3') + bedrock_client = boto3.client('bedrock-runtime') + chunker = SimpleChunker() + model_id = "anthropic.claude-3-sonnet-20240229-v1:0" + + # Extract relevant information from the input event + input_files = event.get('inputFiles') + input_bucket = event.get('bucketName') + + if not all([input_files, input_bucket]): + raise ValueError("Missing required input parameters") + + output_files = [] + + # Iterate over each input file + for input_file in input_files: + content_batches = input_file.get('contentBatches', []) + file_metadata = input_file.get('fileMetadata', {}) + original_file_location = input_file.get('originalFileLocation', {}) + logger.debug(f"Processing input file from: {original_file_location}") + + # original_file_location = input_file['originalFileLocation']['s3_location']['uri'] + s3_uri = original_file_location['s3_location']['uri'] + # bucket, key = original_file_location.replace("s3://", "").split('/', 1) + # Use the extract_bucket_and_key function to get the bucket and key + bucket, key = extract_bucket_and_key(s3_uri) + logger.info(f"Reading PDF from S3: bucket={bucket}, key={key}") + + # Read the PDF from S3 and extract images + pdf_content = read_pdf_from_s3(bucket, key) + images_with_metadata = extract_images_from_pdf(pdf_content) + + # Generate summaries for the extracted images + # prompt = "Summarize all the details in the image, focus on statistics such as bar charts and graphs." + prompt = "Provide a comprehensive description of the image, highlighting all the key details. " \ + "If the image includes charts or any other visual data representations, focus also on summarizing the " \ + "statistical information, including trends, comparisons, and any relavant numerical data present in bar charts, line graphs, or other graphical elements." + + # Generate image summaries and add base64 representations as metadata + processed_images = process_images_with_metadata(bedrock_client, model_id, prompt, images_with_metadata) + + processed_batches = [] + + # Iterate over content batches and process text + for batch in content_batches: + input_key = batch.get('key') + + if not input_key: + raise ValueError("Missing uri in content batch") + + file_content = read_s3_file(s3, input_bucket, input_key) + + # # Update content metadata with page numbers and chunk the content + # for page_number, page_content in enumerate(file_content['fileContents'], start=1): + # page_content['contentMetadata']['pageNumber'] = page_number + + + # Create chunks using the SimpleChunker + chunked_content = process_content(file_content, chunker) + chunked_content['fileContents'].extend(processed_images) # Combine the chunked content with image summaries + logger.debug(f'Chunked content: {json.dumps(chunked_content, indent=2)}') + + + # Define the output key and write the final content to S3 + # final_key = f"OutputLocal/{input_key}" + output_key = f"Output/{input_key}" + + write_to_s3(s3, input_bucket, output_key, chunked_content) + logger.info(f"Final output written to S3 with key: {output_key}") + + # Add processed batch information + processed_batches.append({ + 'key': output_key + }) + + + # Prepare output file information + output_file = { + 'originalFileLocation': original_file_location, + 'fileMetadata': file_metadata, + 'contentBatches': processed_batches + } + + logger.info(f"Processed file: {output_file}") + output_files.append(output_file) + + # Prepare the result with the output files + result = {'outputFiles': output_files} + logger.debug(f'lambda_handler result: {result}') + + logger.debug(f'Final result before returning: {json.dumps(result, indent=2)}') + + return result + +# Helper function to read content from S3 +def read_s3_file(s3_client, bucket: str, key: str) -> dict: + response = s3_client.get_object(Bucket=bucket, Key=key) + return json.loads(response['Body'].read().decode('utf-8')) + +# Helper function to write content to S3 +def write_to_s3(s3_client, bucket: str, key: str, content: dict): + s3_client.put_object(Bucket=bucket, Key=key, Body=json.dumps(content)) + +# Helper function to process content by chunking text +# def process_content(file_content: dict, chunker: Chunker) -> dict: +# chunked_content = {'fileContents': []} + +# for content in file_content.get('fileContents', []): +# chunks = chunker.chunk(content.get('contentBody', '')) +# for chunk in chunks: +# chunked_content['fileContents'].append({ +# 'contentType': content.get('contentType', ''), +# 'contentMetadata': content.get('contentMetadata', {}), +# 'contentBody': chunk +# }) + +# return chunked_content + +def process_content(file_content: dict, chunker: Chunker) -> dict: + chunked_content = { + 'fileContents': [] + } + + for content in file_content.get('fileContents', []): + content_body = content.get('contentBody', '') + content_type = content.get('contentType', '') + content_metadata = content.get('contentMetadata', {}) + + words = content['contentBody'] + chunks = chunker.chunk(words) + + for chunk in chunks: + chunked_content['fileContents'].append({ + 'contentType': content_type, + 'contentMetadata': content_metadata, + 'contentBody': chunk + }) + + return chunked_content + +# Reads a PDF from S3 and returns its content as bytes +def read_pdf_from_s3(bucket: str, key: str) -> bytes: + s3 = boto3.client('s3') + response = s3.get_object(Bucket=bucket, Key=key) + return response['Body'].read() + +# Encodes image data to a base64 string +def encode_image(image_data: bytes) -> str: + return base64.b64encode(image_data).decode('utf-8') + +# Extracts images from a PDF and returns them as a list of dictionaries with metadata +def extract_images_from_pdf(pdf_content: bytes) -> List[dict]: + pdf_reader = PdfReader(BytesIO(pdf_content)) + images = [] + for page_num, page in enumerate(pdf_reader.pages): + for image in page.images: + images.append({ + 'image_data': image.data, + 'contentMetadata': { + # 'pageNumber': page_num + 1, + 'base64Image': encode_image(image.data) + } + }) + return images + +# Sends image data to the Bedrock model and returns the generated summary +def generate_image_summaries(bedrock_client, model_id: str, input_text: str, image_data: bytes) -> dict: + message = { + "role": "user", + "content": [ + {"text": input_text}, + {"image": {"format": 'png', "source": {"bytes": image_data}}} + ] + } + + response = bedrock_client.converse(modelId=model_id, messages=[message]) + return response + +# Processes images by generating summaries and appending metadata +def process_images_with_metadata(bedrock_client, model_id: str, input_text: str, images_with_metadata: List[dict]) -> List[dict]: + results = [] + + for image_metadata in images_with_metadata: + # try: + response = generate_image_summaries(bedrock_client, model_id, input_text, image_metadata['image_data']) + summary = " ".join([content['text'] for content in response['output']['message']['content'] if 'text' in content]) + logger.info(f"Generated summary for image: {summary}") + + results.append({ + "contentType": "PDF", + "contentMetadata": { + # "pageNumber": image_metadata['contentMetadata']['pageNumber'], + "base64Image": image_metadata['contentMetadata']['base64Image'] + }, + "contentBody": summary, + + }) + + # except ClientError as err: + # logger.error(f"A client error occurred: {err.response['Error']['Message']}") + # results.append({ + # "contentMetadata": image_metadata['contentMetadata'], + # "contentBody": "", + # "contentType": "PDF", + # "error": err.response['Error']['Message'] + # }) + + return results + +def extract_bucket_and_key(s3_uri: str): + parsed_url = urlparse(s3_uri) + bucket = parsed_url.netloc + key = parsed_url.path.lstrip('/') + return bucket, key diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/INSTALLER b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/LICENSE b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/LICENSE new file mode 100644 index 00000000..ab327d03 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2006-2008, Mathieu Fenniak +Some contributions copyright (c) 2007, Ashish Kulkarni +Some contributions copyright (c) 2014, Steve Witham + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +* The name of the author may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/METADATA b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/METADATA new file mode 100644 index 00000000..9e521f3d --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/METADATA @@ -0,0 +1,172 @@ +Metadata-Version: 2.1 +Name: pypdf +Version: 5.0.0 +Summary: A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files +Author-email: Mathieu Fenniak +Maintainer-email: Martin Thoma +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Operating System :: OS Independent +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Dist: typing_extensions >= 4.0; python_version < '3.11' +Requires-Dist: dataclasses; python_version < '3.7' +Requires-Dist: cryptography ; extra == "crypto" and ( python_version >= '3.7') +Requires-Dist: PyCryptodome ; extra == "crypto" and ( python_version == '3.6') +Requires-Dist: black ; extra == "dev" +Requires-Dist: pip-tools ; extra == "dev" +Requires-Dist: pre-commit<2.18.0 ; extra == "dev" +Requires-Dist: pytest-cov ; extra == "dev" +Requires-Dist: pytest-socket ; extra == "dev" +Requires-Dist: pytest-timeout ; extra == "dev" +Requires-Dist: flit ; extra == "dev" +Requires-Dist: wheel ; extra == "dev" +Requires-Dist: pytest-xdist ; extra == "dev" +Requires-Dist: sphinx ; extra == "docs" +Requires-Dist: sphinx_rtd_theme ; extra == "docs" +Requires-Dist: myst_parser ; extra == "docs" +Requires-Dist: cryptography ; extra == "full" and ( python_version >= '3.7') +Requires-Dist: PyCryptodome ; extra == "full" and ( python_version == '3.6') +Requires-Dist: Pillow>=8.0.0 ; extra == "full" +Requires-Dist: Pillow>=8.0.0 ; extra == "image" +Project-URL: Bug Reports, https://github.com/py-pdf/pypdf/issues +Project-URL: Changelog, https://pypdf.readthedocs.io/en/latest/meta/CHANGELOG.html +Project-URL: Documentation, https://pypdf.readthedocs.io/en/latest/ +Project-URL: Source, https://github.com/py-pdf/pypdf +Provides-Extra: crypto +Provides-Extra: dev +Provides-Extra: docs +Provides-Extra: full +Provides-Extra: image + +[![PyPI version](https://badge.fury.io/py/pypdf.svg)](https://badge.fury.io/py/pypdf) +[![Python Support](https://img.shields.io/pypi/pyversions/pypdf.svg)](https://pypi.org/project/pypdf/) +[![](https://img.shields.io/badge/-documentation-green)](https://pypdf.readthedocs.io/en/stable/) +[![GitHub last commit](https://img.shields.io/github/last-commit/py-pdf/pypdf)](https://github.com/py-pdf/pypdf) +[![codecov](https://codecov.io/gh/py-pdf/pypdf/branch/main/graph/badge.svg?token=id42cGNZ5Z)](https://codecov.io/gh/py-pdf/pypdf) + +# pypdf + +pypdf is a free and open-source pure-python PDF library capable of splitting, +[merging](https://pypdf.readthedocs.io/en/stable/user/merging-pdfs.html), +[cropping, and transforming](https://pypdf.readthedocs.io/en/stable/user/cropping-and-transforming.html) +the pages of PDF files. It can also add +custom data, viewing options, and +[passwords](https://pypdf.readthedocs.io/en/stable/user/encryption-decryption.html) +to PDF files. pypdf can +[retrieve text](https://pypdf.readthedocs.io/en/stable/user/extract-text.html) +and +[metadata](https://pypdf.readthedocs.io/en/stable/user/metadata.html) +from PDFs as well. + +See [pdfly](https://github.com/py-pdf/pdfly) for a CLI application that uses pypdf to interact with PDFs. + +## Installation + +Install pypdf using pip: + +``` +pip install pypdf +``` + +For using pypdf with AES encryption or decryption, install extra dependencies: + +``` +pip install pypdf[crypto] +``` + +> **NOTE**: `pypdf` 3.1.0 and above include significant improvements compared to +> previous versions. Please refer to [the migration +> guide](https://pypdf.readthedocs.io/en/latest/user/migration-1-to-2.html) for +> more information. + +## Usage + +```python +from pypdf import PdfReader + +reader = PdfReader("example.pdf") +number_of_pages = len(reader.pages) +page = reader.pages[0] +text = page.extract_text() +``` + +pypdf can do a lot more, e.g. splitting, merging, reading and creating +annotations, decrypting and encrypting, and more. Check out [the +documentation](https://pypdf.readthedocs.io/en/stable/) for additional usage +examples! + +For questions and answers, visit +[StackOverflow](https://stackoverflow.com/questions/tagged/pypdf) +(tagged with [pypdf](https://stackoverflow.com/questions/tagged/pypdf)). + +## Contributions + +Maintaining pypdf is a collaborative effort. You can support the project by +writing documentation, helping to narrow down issues, and submitting code. +See the [CONTRIBUTING.md](https://github.com/py-pdf/pypdf/blob/main/CONTRIBUTING.md) file for more information. + +### Q&A + +The experience pypdf users have covers the whole range from beginners who +want to make their live easier to experts who developed software before PDF +existed. You can contribute to the pypdf community by answering questions +on [StackOverflow](https://stackoverflow.com/questions/tagged/pypdf), +helping in [discussions](https://github.com/py-pdf/pypdf/discussions), +and asking users who report issues for [MCVE](https://stackoverflow.com/help/minimal-reproducible-example)'s (Code + example PDF!). + + +### Issues + +A good bug ticket includes a MCVE - a minimal complete verifiable example. +For pypdf, this means that you must upload a PDF that causes the bug to occur +as well as the code you're executing with all of the output. Use +`print(pypdf.__version__)` to tell us which version you're using. + +### Code + +All code contributions are welcome, but smaller ones have a better chance to +get included in a timely manner. Adding unit tests for new features or test +cases for bugs you've fixed help us to ensure that the Pull Request (PR) is fine. + +pypdf includes a test suite which can be executed with `pytest`: + +```bash +$ pytest +===================== test session starts ===================== +platform linux -- Python 3.6.15, pytest-7.0.1, pluggy-1.0.0 +rootdir: /home/moose/GitHub/Martin/pypdf +plugins: cov-3.0.0 +collected 233 items + +tests/test_basic_features.py .. [ 0%] +tests/test_constants.py . [ 1%] +tests/test_filters.py .................x..... [ 11%] +tests/test_generic.py ................................. [ 25%] +............. [ 30%] +tests/test_javascript.py .. [ 31%] +tests/test_merger.py . [ 32%] +tests/test_page.py ......................... [ 42%] +tests/test_pagerange.py ................ [ 49%] +tests/test_papersizes.py .................. [ 57%] +tests/test_reader.py .................................. [ 72%] +............... [ 78%] +tests/test_utils.py .................... [ 87%] +tests/test_workflows.py .......... [ 91%] +tests/test_writer.py ................. [ 98%] +tests/test_xmp.py ... [100%] + +========== 232 passed, 1 xfailed, 1 warning in 4.52s ========== +``` + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/RECORD b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/RECORD new file mode 100644 index 00000000..5c319b3b --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/RECORD @@ -0,0 +1,109 @@ +pypdf-5.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pypdf-5.0.0.dist-info/LICENSE,sha256=qXrCMOXzPvEKU2eoUOsB-R8aCwZONHQsd5TSKUVX9SQ,1605 +pypdf-5.0.0.dist-info/METADATA,sha256=89e0xYeXjJZjS803ePtCWvfee32uVj6PMASpKL5en80,7404 +pypdf-5.0.0.dist-info/RECORD,, +pypdf-5.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pypdf-5.0.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +pypdf/__init__.py,sha256=chEvj_DbuU93PWHkjh-rRk9Aa4LiL-ecjqToCZASFoU,1316 +pypdf/__pycache__/__init__.cpython-38.pyc,, +pypdf/__pycache__/_cmap.cpython-38.pyc,, +pypdf/__pycache__/_doc_common.cpython-38.pyc,, +pypdf/__pycache__/_encryption.cpython-38.pyc,, +pypdf/__pycache__/_merger.cpython-38.pyc,, +pypdf/__pycache__/_page.cpython-38.pyc,, +pypdf/__pycache__/_page_labels.cpython-38.pyc,, +pypdf/__pycache__/_protocols.cpython-38.pyc,, +pypdf/__pycache__/_reader.cpython-38.pyc,, +pypdf/__pycache__/_utils.cpython-38.pyc,, +pypdf/__pycache__/_version.cpython-38.pyc,, +pypdf/__pycache__/_writer.cpython-38.pyc,, +pypdf/__pycache__/_xobj_image_helpers.cpython-38.pyc,, +pypdf/__pycache__/constants.cpython-38.pyc,, +pypdf/__pycache__/errors.cpython-38.pyc,, +pypdf/__pycache__/filters.cpython-38.pyc,, +pypdf/__pycache__/pagerange.cpython-38.pyc,, +pypdf/__pycache__/papersizes.cpython-38.pyc,, +pypdf/__pycache__/types.cpython-38.pyc,, +pypdf/__pycache__/xmp.cpython-38.pyc,, +pypdf/_cmap.py,sha256=64myS_koQ-qIC1quZM-h8G9sWZc6A1XwrAEDfnjEMTc,17977 +pypdf/_codecs/__init__.py,sha256=15Fls0Fzl2NXKJyGNO4ozWveYCbOtDkdFiUSUpxHVGQ,1674 +pypdf/_codecs/__pycache__/__init__.cpython-38.pyc,, +pypdf/_codecs/__pycache__/adobe_glyphs.cpython-38.pyc,, +pypdf/_codecs/__pycache__/pdfdoc.cpython-38.pyc,, +pypdf/_codecs/__pycache__/std.cpython-38.pyc,, +pypdf/_codecs/__pycache__/symbol.cpython-38.pyc,, +pypdf/_codecs/__pycache__/zapfding.cpython-38.pyc,, +pypdf/_codecs/adobe_glyphs.py,sha256=jrMZTzGFE8aMEuwfNJ4JZh_GZypPBg6SLE1oaC9DRTU,447237 +pypdf/_codecs/pdfdoc.py,sha256=xfSvMFYsvxuaSQ0Uu9vZDKaB0Wu85h1uCiB1i9rAcUU,4269 +pypdf/_codecs/std.py,sha256=DyQMuEpAGEpS9uy1jWf4cnj-kqShPOAij5sI7Q1YD8E,2630 +pypdf/_codecs/symbol.py,sha256=nIaGQIlhWCJiPMHrwUlmGHH-_fOXyEKvguRmuKXcGAk,3734 +pypdf/_codecs/zapfding.py,sha256=PQxjxRC616d41xF3exVxP1W8nM4QrZfjO3lmtLxpE_s,3742 +pypdf/_crypt_providers/__init__.py,sha256=O6cOQ1QYca10IV_YDo1RE6PzCs-rxL9pNGmP__nRGkE,3054 +pypdf/_crypt_providers/__pycache__/__init__.cpython-38.pyc,, +pypdf/_crypt_providers/__pycache__/_base.cpython-38.pyc,, +pypdf/_crypt_providers/__pycache__/_cryptography.cpython-38.pyc,, +pypdf/_crypt_providers/__pycache__/_fallback.cpython-38.pyc,, +pypdf/_crypt_providers/__pycache__/_pycryptodome.cpython-38.pyc,, +pypdf/_crypt_providers/_base.py,sha256=_f53Mj6vivhEZMQ4vNxN5G0IOgFY-n5_leke0c_qiNU,1711 +pypdf/_crypt_providers/_cryptography.py,sha256=zT3WmbPzesvgHRkGcKAldqJ24MY3BwZViVbSc55Zxhw,4557 +pypdf/_crypt_providers/_fallback.py,sha256=PVDQQrq389VbaBqOHxXfoyCk9bLYgFrrDKVpNXzTdx8,3345 +pypdf/_crypt_providers/_pycryptodome.py,sha256=U1aQZ9iYBrZo-hKCjJUhGOPhwEFToiitowQ316TNrrA,3381 +pypdf/_doc_common.py,sha256=IK5-UtIE2zOcQeuUM9QprOhakbb0bP8pByjH_GtVJmI,51435 +pypdf/_encryption.py,sha256=k8tOeUP_wyroS0pi4BObZ1Lr1ZDkQzNNjgtgAStX40c,48902 +pypdf/_merger.py,sha256=YfSQKDiiQz2WtCmVZjxP_nv2pR2shiBf2tDiAb41c7s,1744 +pypdf/_page.py,sha256=bYzOGolIG8Cisyoe6OU-YUAwtA5QiIqEDD5OR1SlYq8,98281 +pypdf/_page_labels.py,sha256=xhXUDz54FevRnyGbF0M0AliWdBY3zNHZLSe1FhMf1F8,8542 +pypdf/_protocols.py,sha256=noE1y2fVE-z1wq-FkQzaS5exa8ovOFTUXqdQSvqi57c,2142 +pypdf/_reader.py,sha256=y6Duk9KZjqf5OeYgiPakUsjzvzDIijFLik7mCcZ-h04,48096 +pypdf/_text_extraction/__init__.py,sha256=YmSRITJ4VqdWlbTY9dJ57OWTfB1bUmdcQJCEydZB8Bc,10373 +pypdf/_text_extraction/__pycache__/__init__.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/__init__.py,sha256=BPWqqo0ggSKQf-2owsaN_vVvyqsnaZlpIWY3N1bzegc,338 +pypdf/_text_extraction/_layout_mode/__pycache__/__init__.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/__pycache__/_fixed_width_page.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/__pycache__/_font.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/__pycache__/_font_widths.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_manager.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_params.cpython-38.pyc,, +pypdf/_text_extraction/_layout_mode/_fixed_width_page.py,sha256=2_v7giqsBln4qGeVagwsXbt2uXTsmTjpl5e2LavpFjo,14658 +pypdf/_text_extraction/_layout_mode/_font.py,sha256=oxICBjlLr7LEeXXQ99bRCjfaPzjQTtcLoFvg_8q2y3I,6117 +pypdf/_text_extraction/_layout_mode/_font_widths.py,sha256=f4Q1acWC_iiLhzKjyfoF8_FFz7wlRlLtXE1Vs6Ifsbo,4264 +pypdf/_text_extraction/_layout_mode/_text_state_manager.py,sha256=DvYiT14AD1Fn7N-cZFLBEWeL_rxXsJIbXLZfraCNG50,8012 +pypdf/_text_extraction/_layout_mode/_text_state_params.py,sha256=oKVgKfU_dcoDJQr8k_yqQaoXRqFjyad7IC9JoW4S0Fc,5316 +pypdf/_utils.py,sha256=P1Uj-dtNWB8j4yTStvi2iHz45oBA4FkdsVl2oq9XqiM,19136 +pypdf/_version.py,sha256=DgiwC5i6Qn9S9SAS5v73GdLwuTrTF6Uxx0LBYewmzI8,22 +pypdf/_writer.py,sha256=sMFNWPSMgP0E0EwMt84IW3MRT71Feh4qbfj5C3s8M2w,127294 +pypdf/_xobj_image_helpers.py,sha256=NRrzx_EcS5aKreT9xA1Nfx8PppfIOzR6D_5IKqR_tHo,11441 +pypdf/annotations/__init__.py,sha256=oh8Z0IZoFHqP3-h5XVM_3CRFVmz6hyFRSpKZjtp1lhs,1114 +pypdf/annotations/__pycache__/__init__.cpython-38.pyc,, +pypdf/annotations/__pycache__/_base.cpython-38.pyc,, +pypdf/annotations/__pycache__/_markup_annotations.cpython-38.pyc,, +pypdf/annotations/__pycache__/_non_markup_annotations.cpython-38.pyc,, +pypdf/annotations/_base.py,sha256=mpNLTV4AuiqYO6Q6EqcJDWowQfm3QAjW_Rs1qm37Kxo,905 +pypdf/annotations/_markup_annotations.py,sha256=hD-WOEjNhwfCyKP4RHurQoRT5SuQeJ4SnPFo_4axddY,9827 +pypdf/annotations/_non_markup_annotations.py,sha256=OHs7FGUpNCcDbayijgN6YPYWbZMZSMl-h-Zgwk8Ai7I,3606 +pypdf/constants.py,sha256=zlklbQQnYYQBOH1NTFF_JzTKy86xulqM34QeycrJcco,21115 +pypdf/errors.py,sha256=_wfmOGHFct1BGSs9SRdY_S1HrQTnUYBASt_d-au0IqI,1740 +pypdf/filters.py,sha256=q52S3zZ-N99vTN0iYsRKzFx7kkE3XzdLm51AqDwv9eE,31329 +pypdf/generic/__init__.py,sha256=_lOEhPnoXhDJtTkojQUVxF6iE-Owq5QNdDtW-OU3Rbw,7226 +pypdf/generic/__pycache__/__init__.cpython-38.pyc,, +pypdf/generic/__pycache__/_base.cpython-38.pyc,, +pypdf/generic/__pycache__/_data_structures.cpython-38.pyc,, +pypdf/generic/__pycache__/_fit.cpython-38.pyc,, +pypdf/generic/__pycache__/_image_inline.cpython-38.pyc,, +pypdf/generic/__pycache__/_outline.cpython-38.pyc,, +pypdf/generic/__pycache__/_rectangle.cpython-38.pyc,, +pypdf/generic/__pycache__/_utils.cpython-38.pyc,, +pypdf/generic/__pycache__/_viewerpref.cpython-38.pyc,, +pypdf/generic/_base.py,sha256=_8owXghdvy29kLHBHguOr36GzShHCEo1-Y0V-kaEXp0,29540 +pypdf/generic/_data_structures.py,sha256=hjKSNHJzVvSfv7gtudLX_L08czvSzTDaQAhiBxwfUwY,59444 +pypdf/generic/_fit.py,sha256=TUhWzZ6KVTM-8qBtfvvAdWVZFLttD7AhL7VhUlC6hJg,5426 +pypdf/generic/_image_inline.py,sha256=w8_tLva9vK41RGtV20Plebfrf0nzN6NskRlcrMkd4XE,8514 +pypdf/generic/_outline.py,sha256=b8NsPZeDaO_s1ZRILwKFPEaofAhhyfDGOS06cm4YxRk,1090 +pypdf/generic/_rectangle.py,sha256=5KJRbQESqdzrYvJOFcwfp0_v_bhCDVj9r4yMyGXSGyc,3808 +pypdf/generic/_utils.py,sha256=dy52zuCjOHby1qUQ_wB0kPGHcQaEfNCkpuRCszgzKI0,7450 +pypdf/generic/_viewerpref.py,sha256=6hdRlGAiygH_Bvaz-p-UEU1iHJSFBNAbX0KPQsWIvtA,6684 +pypdf/pagerange.py,sha256=EkfHDNNsF6d0SYVI5ftq3QjbEqB1UlAWHgyh6cYjKKg,6881 +pypdf/papersizes.py,sha256=6Tz5sfNN_3JOUapY83U-lakohnpXYA0hSEQNmOVLFL8,1413 +pypdf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pypdf/types.py,sha256=6B6pMncEhcqFfq-iKs5IBPg6guWXffU6YHpeYzCJH-s,1963 +pypdf/xmp.py,sha256=pNhZv1yPax5F4Y2cY4DVXkqKKoZquqnHOe4RFMKZBOs,14244 diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/REQUESTED b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/WHEEL b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/WHEEL new file mode 100644 index 00000000..3b5e64b5 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf-5.0.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__init__.py new file mode 100644 index 00000000..6a02b60d --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__init__.py @@ -0,0 +1,49 @@ +""" +pypdf is a free and open-source pure-python PDF library capable of splitting, +merging, cropping, and transforming the pages of PDF files. It can also add +custom data, viewing options, and passwords to PDF files. pypdf can retrieve +text and metadata from PDFs as well. + +You can read the full docs at https://pypdf.readthedocs.io/. +""" + +from ._crypt_providers import crypt_provider +from ._doc_common import DocumentInformation +from ._encryption import PasswordType +from ._merger import PdfMerger +from ._page import PageObject, Transformation, mult +from ._reader import PdfReader +from ._version import __version__ +from ._writer import ObjectDeletionFlag, PdfWriter +from .constants import ImageType +from .pagerange import PageRange, parse_filename_page_ranges +from .papersizes import PaperSize + +try: + import PIL + + pil_version = PIL.__version__ +except ImportError: + pil_version = "none" + +_debug_versions = ( + f"pypdf=={__version__}, crypt_provider={crypt_provider}, PIL={pil_version}" +) + +__all__ = [ + "__version__", + "_debug_versions", + "ImageType", + "mult", + "PageRange", + "PaperSize", + "DocumentInformation", + "ObjectDeletionFlag", + "parse_filename_page_ranges", + "PdfMerger", + "PdfReader", + "PdfWriter", + "Transformation", + "PageObject", + "PasswordType", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..8b924cff Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_cmap.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_cmap.cpython-38.pyc new file mode 100644 index 00000000..a053444b Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_cmap.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_doc_common.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_doc_common.cpython-38.pyc new file mode 100644 index 00000000..90d69700 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_doc_common.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_encryption.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_encryption.cpython-38.pyc new file mode 100644 index 00000000..12cc65e0 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_encryption.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_merger.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_merger.cpython-38.pyc new file mode 100644 index 00000000..8c35739d Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_merger.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_page.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_page.cpython-38.pyc new file mode 100644 index 00000000..abda019a Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_page.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_page_labels.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_page_labels.cpython-38.pyc new file mode 100644 index 00000000..40871fb9 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_page_labels.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_protocols.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_protocols.cpython-38.pyc new file mode 100644 index 00000000..321f02e4 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_protocols.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_reader.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_reader.cpython-38.pyc new file mode 100644 index 00000000..189006f4 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_reader.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_utils.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_utils.cpython-38.pyc new file mode 100644 index 00000000..62fd63c0 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_utils.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_version.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_version.cpython-38.pyc new file mode 100644 index 00000000..34f37708 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_version.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_writer.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_writer.cpython-38.pyc new file mode 100644 index 00000000..f2f97d66 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_writer.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_xobj_image_helpers.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_xobj_image_helpers.cpython-38.pyc new file mode 100644 index 00000000..346c46f3 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/_xobj_image_helpers.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/constants.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/constants.cpython-38.pyc new file mode 100644 index 00000000..290a66cb Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/constants.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/errors.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/errors.cpython-38.pyc new file mode 100644 index 00000000..8b597970 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/errors.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/filters.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/filters.cpython-38.pyc new file mode 100644 index 00000000..1b586ad4 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/filters.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/pagerange.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/pagerange.cpython-38.pyc new file mode 100644 index 00000000..7fdb1873 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/pagerange.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/papersizes.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/papersizes.cpython-38.pyc new file mode 100644 index 00000000..e41d20e5 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/papersizes.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/types.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/types.cpython-38.pyc new file mode 100644 index 00000000..2bda1d84 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/types.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/xmp.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/xmp.cpython-38.pyc new file mode 100644 index 00000000..7ce5e96a Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/__pycache__/xmp.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_cmap.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_cmap.py new file mode 100644 index 00000000..dcf3678b --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_cmap.py @@ -0,0 +1,515 @@ +from binascii import unhexlify +from math import ceil +from typing import Any, Dict, List, Tuple, Union, cast + +from ._codecs import adobe_glyphs, charset_encoding +from ._utils import logger_error, logger_warning +from .generic import ( + DecodedStreamObject, + DictionaryObject, + StreamObject, + is_null_or_none, +) + + +# code freely inspired from @twiggy ; see #711 +def build_char_map( + font_name: str, space_width: float, obj: DictionaryObject +) -> Tuple[str, float, Union[str, Dict[int, str]], Dict[Any, Any], DictionaryObject]: + """ + Determine information about a font. + + Args: + font_name: font name as a string + space_width: default space width if no data is found. + obj: XObject or Page where you can find a /Resource dictionary + + Returns: + Font sub-type, space_width criteria (50% of width), encoding, map character-map, font-dictionary. + The font-dictionary itself is suitable for the curious. + """ + ft: DictionaryObject = obj["/Resources"]["/Font"][font_name] # type: ignore + font_subtype, font_halfspace, font_encoding, font_map = build_char_map_from_dict( + space_width, ft + ) + return font_subtype, font_halfspace, font_encoding, font_map, ft + + +def build_char_map_from_dict( + space_width: float, ft: DictionaryObject +) -> Tuple[str, float, Union[str, Dict[int, str]], Dict[Any, Any]]: + """ + Determine information about a font. + + Args: + space_width: default space with if no data found + (normally half the width of a character). + ft: Font Dictionary + + Returns: + Font sub-type, space_width criteria(50% of width), encoding, map character-map. + The font-dictionary itself is suitable for the curious. + """ + font_type: str = cast(str, ft["/Subtype"]) + + space_code = 32 + encoding, space_code = parse_encoding(ft, space_code) + map_dict, space_code, int_entry = parse_to_unicode(ft, space_code) + + # encoding can be either a string for decode + # (on 1,2 or a variable number of bytes) of a char table (for 1 byte only for me) + # if empty string, it means it is than encoding field is not present and + # we have to select the good encoding from cmap input data + if encoding == "": + if -1 not in map_dict or map_dict[-1] == 1: + # I have not been able to find any rule for no /Encoding nor /ToUnicode + # One example shows /Symbol,bold I consider 8 bits encoding default + encoding = "charmap" + else: + encoding = "utf-16-be" + # apply rule from PDF ref 1.7 §5.9.1, 1st bullet : + # if cmap not empty encoding should be discarded + # (here transformed into identity for those characters) + # if encoding is an str it is expected to be a identity translation + elif isinstance(encoding, dict): + for x in int_entry: + if x <= 255: + encoding[x] = chr(x) + try: + # override space_width with new params + space_width = _default_fonts_space_width[cast(str, ft["/BaseFont"])] + except Exception: + pass + # I consider the space_code is available on one byte + if isinstance(space_code, str): + try: # one byte + sp = space_code.encode("charmap")[0] + except Exception: + sp = space_code.encode("utf-16-be") + sp = sp[0] + 256 * sp[1] + else: + sp = space_code + sp_width = compute_space_width(ft, sp, space_width) + + return ( + font_type, + float(sp_width / 2), + encoding, + # https://github.com/python/mypy/issues/4374 + map_dict, + ) + + +# used when missing data, e.g. font def missing +unknown_char_map: Tuple[str, float, Union[str, Dict[int, str]], Dict[Any, Any]] = ( + "Unknown", + 9999, + dict(zip(range(256), ["�"] * 256)), + {}, +) + + +_predefined_cmap: Dict[str, str] = { + "/Identity-H": "utf-16-be", + "/Identity-V": "utf-16-be", + "/GB-EUC-H": "gbk", + "/GB-EUC-V": "gbk", + "/GBpc-EUC-H": "gb2312", + "/GBpc-EUC-V": "gb2312", + "/GBK-EUC-H": "gbk", + "/GBK-EUC-V": "gbk", + "/GBK2K-H": "gb18030", + "/GBK2K-V": "gb18030", + "/ETen-B5-H": "cp950", + "/ETen-B5-V": "cp950", + "/ETenms-B5-H": "cp950", + "/ETenms-B5-V": "cp950", + "/UniCNS-UTF16-H": "utf-16-be", + "/UniCNS-UTF16-V": "utf-16-be", + "/UniGB-UTF16-H": "gb18030", + "/UniGB-UTF16-V": "gb18030", + # UCS2 in code +} + +# manually extracted from http://mirrors.ctan.org/fonts/adobe/afm/Adobe-Core35_AFMs-229.tar.gz +_default_fonts_space_width: Dict[str, int] = { + "/Courier": 600, + "/Courier-Bold": 600, + "/Courier-BoldOblique": 600, + "/Courier-Oblique": 600, + "/Helvetica": 278, + "/Helvetica-Bold": 278, + "/Helvetica-BoldOblique": 278, + "/Helvetica-Oblique": 278, + "/Helvetica-Narrow": 228, + "/Helvetica-NarrowBold": 228, + "/Helvetica-NarrowBoldOblique": 228, + "/Helvetica-NarrowOblique": 228, + "/Times-Roman": 250, + "/Times-Bold": 250, + "/Times-BoldItalic": 250, + "/Times-Italic": 250, + "/Symbol": 250, + "/ZapfDingbats": 278, +} + + +def parse_encoding( + ft: DictionaryObject, space_code: int +) -> Tuple[Union[str, Dict[int, str]], int]: + encoding: Union[str, List[str], Dict[int, str]] = [] + if "/Encoding" not in ft: + try: + if "/BaseFont" in ft and cast(str, ft["/BaseFont"]) in charset_encoding: + encoding = dict( + zip(range(256), charset_encoding[cast(str, ft["/BaseFont"])]) + ) + else: + encoding = "charmap" + return encoding, _default_fonts_space_width[cast(str, ft["/BaseFont"])] + except Exception: + if cast(str, ft["/Subtype"]) == "/Type1": + return "charmap", space_code + else: + return "", space_code + enc: Union(str, DictionaryObject) = ft["/Encoding"].get_object() # type: ignore + if isinstance(enc, str): + try: + # already done : enc = NameObject.unnumber(enc.encode()).decode() + # for #xx decoding + if enc in charset_encoding: + encoding = charset_encoding[enc].copy() + elif enc in _predefined_cmap: + encoding = _predefined_cmap[enc] + elif "-UCS2-" in enc: + encoding = "utf-16-be" + else: + raise Exception("not found") + except Exception: + logger_error(f"Advanced encoding {enc} not implemented yet", __name__) + encoding = enc + elif isinstance(enc, DictionaryObject) and "/BaseEncoding" in enc: + try: + encoding = charset_encoding[cast(str, enc["/BaseEncoding"])].copy() + except Exception: + logger_error( + f"Advanced encoding {encoding} not implemented yet", + __name__, + ) + encoding = charset_encoding["/StandardCoding"].copy() + else: + encoding = charset_encoding["/StandardCoding"].copy() + if "/Differences" in enc: + x: int = 0 + o: Union[int, str] + for o in cast(DictionaryObject, cast(DictionaryObject, enc)["/Differences"]): + if isinstance(o, int): + x = o + else: # isinstance(o,str): + try: + encoding[x] = adobe_glyphs[o] # type: ignore + except Exception: + encoding[x] = o # type: ignore + if o == " ": + space_code = x + x += 1 + if isinstance(encoding, list): + encoding = dict(zip(range(256), encoding)) + return encoding, space_code + + +def parse_to_unicode( + ft: DictionaryObject, space_code: int +) -> Tuple[Dict[Any, Any], int, List[int]]: + # will store all translation code + # and map_dict[-1] we will have the number of bytes to convert + map_dict: Dict[Any, Any] = {} + + # will provide the list of cmap keys as int to correct encoding + int_entry: List[int] = [] + + if "/ToUnicode" not in ft: + if ft.get("/Subtype", "") == "/Type1": + return type1_alternative(ft, map_dict, space_code, int_entry) + else: + return {}, space_code, [] + process_rg: bool = False + process_char: bool = False + multiline_rg: Union[ + None, Tuple[int, int] + ] = None # tuple = (current_char, remaining size) ; cf #1285 for example of file + cm = prepare_cm(ft) + for line in cm.split(b"\n"): + process_rg, process_char, multiline_rg = process_cm_line( + line.strip(b" \t"), + process_rg, + process_char, + multiline_rg, + map_dict, + int_entry, + ) + + for a, value in map_dict.items(): + if value == " ": + space_code = a + return map_dict, space_code, int_entry + + +def prepare_cm(ft: DictionaryObject) -> bytes: + tu = ft["/ToUnicode"] + cm: bytes + if isinstance(tu, StreamObject): + cm = cast(DecodedStreamObject, ft["/ToUnicode"]).get_data() + else: # if (tu is None) or cast(str, tu).startswith("/Identity"): + # the full range 0000-FFFF will be processed + cm = b"beginbfrange\n<0000> <0001> <0000>\nendbfrange" + if isinstance(cm, str): + cm = cm.encode() + # we need to prepare cm before due to missing return line in pdf printed + # to pdf from word + cm = ( + cm.strip() + .replace(b"beginbfchar", b"\nbeginbfchar\n") + .replace(b"endbfchar", b"\nendbfchar\n") + .replace(b"beginbfrange", b"\nbeginbfrange\n") + .replace(b"endbfrange", b"\nendbfrange\n") + .replace(b"<<", b"\n{\n") # text between << and >> not used but + .replace(b">>", b"\n}\n") # some solution to find it back + ) + ll = cm.split(b"<") + for i in range(len(ll)): + j = ll[i].find(b">") + if j >= 0: + if j == 0: + # string is empty: stash a placeholder here (see below) + # see https://github.com/py-pdf/pypdf/issues/1111 + content = b"." + else: + content = ll[i][:j].replace(b" ", b"") + ll[i] = content + b" " + ll[i][j + 1 :] + cm = ( + (b" ".join(ll)) + .replace(b"[", b" [ ") + .replace(b"]", b" ]\n ") + .replace(b"\r", b"\n") + ) + return cm + + +def process_cm_line( + line: bytes, + process_rg: bool, + process_char: bool, + multiline_rg: Union[None, Tuple[int, int]], + map_dict: Dict[Any, Any], + int_entry: List[int], +) -> Tuple[bool, bool, Union[None, Tuple[int, int]]]: + if line == b"" or line[0] == 37: # 37 = % + return process_rg, process_char, multiline_rg + line = line.replace(b"\t", b" ") + if b"beginbfrange" in line: + process_rg = True + elif b"endbfrange" in line: + process_rg = False + elif b"beginbfchar" in line: + process_char = True + elif b"endbfchar" in line: + process_char = False + elif process_rg: + multiline_rg = parse_bfrange(line, map_dict, int_entry, multiline_rg) + elif process_char: + parse_bfchar(line, map_dict, int_entry) + return process_rg, process_char, multiline_rg + + +def parse_bfrange( + line: bytes, + map_dict: Dict[Any, Any], + int_entry: List[int], + multiline_rg: Union[None, Tuple[int, int]], +) -> Union[None, Tuple[int, int]]: + lst = [x for x in line.split(b" ") if x] + closure_found = False + if multiline_rg is not None: + fmt = b"%%0%dX" % (map_dict[-1] * 2) + a = multiline_rg[0] # a, b not in the current line + b = multiline_rg[1] + for sq in lst[0:]: + if sq == b"]": + closure_found = True + break + map_dict[ + unhexlify(fmt % a).decode( + "charmap" if map_dict[-1] == 1 else "utf-16-be", + "surrogatepass", + ) + ] = unhexlify(sq).decode("utf-16-be", "surrogatepass") + int_entry.append(a) + a += 1 + else: + a = int(lst[0], 16) + b = int(lst[1], 16) + nbi = max(len(lst[0]), len(lst[1])) + map_dict[-1] = ceil(nbi / 2) + fmt = b"%%0%dX" % (map_dict[-1] * 2) + if lst[2] == b"[": + for sq in lst[3:]: + if sq == b"]": + closure_found = True + break + map_dict[ + unhexlify(fmt % a).decode( + "charmap" if map_dict[-1] == 1 else "utf-16-be", + "surrogatepass", + ) + ] = unhexlify(sq).decode("utf-16-be", "surrogatepass") + int_entry.append(a) + a += 1 + else: # case without list + c = int(lst[2], 16) + fmt2 = b"%%0%dX" % max(4, len(lst[2])) + closure_found = True + while a <= b: + map_dict[ + unhexlify(fmt % a).decode( + "charmap" if map_dict[-1] == 1 else "utf-16-be", + "surrogatepass", + ) + ] = unhexlify(fmt2 % c).decode("utf-16-be", "surrogatepass") + int_entry.append(a) + a += 1 + c += 1 + return None if closure_found else (a, b) + + +def parse_bfchar(line: bytes, map_dict: Dict[Any, Any], int_entry: List[int]) -> None: + lst = [x for x in line.split(b" ") if x] + map_dict[-1] = len(lst[0]) // 2 + while len(lst) > 1: + map_to = "" + # placeholder (see above) means empty string + if lst[1] != b".": + map_to = unhexlify(lst[1]).decode( + "charmap" if len(lst[1]) < 4 else "utf-16-be", "surrogatepass" + ) # join is here as some cases where the code was split + map_dict[ + unhexlify(lst[0]).decode( + "charmap" if map_dict[-1] == 1 else "utf-16-be", "surrogatepass" + ) + ] = map_to + int_entry.append(int(lst[0], 16)) + lst = lst[2:] + + +def compute_space_width( + ft: DictionaryObject, space_code: int, space_width: float +) -> float: + sp_width: float = space_width * 2.0 # default value + w = [] + w1 = {} + st: int = 0 + if "/DescendantFonts" in ft: # ft["/Subtype"].startswith("/CIDFontType"): + ft1 = ft["/DescendantFonts"][0].get_object() # type: ignore + try: + w1[-1] = cast(float, ft1["/DW"]) + except Exception: + w1[-1] = 1000.0 + if "/W" in ft1: + w = list(ft1["/W"]) + else: + w = [] + while len(w) > 0: + st = w[0] if isinstance(w[0], int) else w[0].get_object() + second = w[1].get_object() + if isinstance(second, int): + for x in range(st, second): + w1[x] = w[2] + w = w[3:] + elif isinstance(second, list): + for y in second: + w1[st] = y + st += 1 + w = w[2:] + else: + logger_warning( + "unknown widths : \n" + (ft1["/W"]).__repr__(), + __name__, + ) + break + try: + sp_width = w1[space_code] + except Exception: + sp_width = ( + w1[-1] / 2.0 + ) # if using default we consider space will be only half size + elif "/Widths" in ft: + w = list(ft["/Widths"]) # type: ignore + try: + st = cast(int, ft["/FirstChar"]) + en: int = cast(int, ft["/LastChar"]) + if st > space_code or en < space_code: + raise Exception("Not in range") + if w[space_code - st].get_object() == 0: + raise Exception("null width") + sp_width = w[space_code - st].get_object() + except Exception: + if "/FontDescriptor" in ft and "/MissingWidth" in cast( + DictionaryObject, ft["/FontDescriptor"] + ): + sp_width = ft["/FontDescriptor"]["/MissingWidth"].get_object() # type: ignore + else: + # will consider width of char as avg(width)/2 + m = 0 + cpt = 0 + for xx in w: + xx = xx.get_object() + if xx > 0: + m += xx + cpt += 1 + sp_width = m / max(1, cpt) / 2 + + if is_null_or_none(sp_width): + sp_width = 0.0 + return sp_width + + +def type1_alternative( + ft: DictionaryObject, + map_dict: Dict[Any, Any], + space_code: int, + int_entry: List[int], +) -> Tuple[Dict[Any, Any], int, List[int]]: + if "/FontDescriptor" not in ft: + return map_dict, space_code, int_entry + ft_desc = cast(DictionaryObject, ft["/FontDescriptor"]).get("/FontFile") + if is_null_or_none(ft_desc): + return map_dict, space_code, int_entry + assert ft_desc is not None, "mypy" + txt = ft_desc.get_object().get_data() + txt = txt.split(b"eexec\n")[0] # only clear part + txt = txt.split(b"/Encoding")[1] # to get the encoding part + lines = txt.replace(b"\r", b"\n").split(b"\n") + for li in lines: + if li.startswith(b"dup"): + words = [_w for _w in li.split(b" ") if _w != b""] + if len(words) > 3 and words[3] != b"put": + continue + try: + i = int(words[1]) + except ValueError: # pragma: no cover + continue + try: + v = adobe_glyphs[words[2].decode()] + except KeyError: + if words[2].startswith(b"/uni"): + try: + v = chr(int(words[2][4:], 16)) + except ValueError: # pragma: no cover + continue + else: + continue + if words[2].decode() == b" ": + space_code = i + map_dict[chr(i)] = v + int_entry.append(i) + return map_dict, space_code, int_entry diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__init__.py new file mode 100644 index 00000000..70d8e666 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__init__.py @@ -0,0 +1,61 @@ +from typing import Dict, List + +from .adobe_glyphs import adobe_glyphs +from .pdfdoc import _pdfdoc_encoding +from .std import _std_encoding +from .symbol import _symbol_encoding +from .zapfding import _zapfding_encoding + + +def fill_from_encoding(enc: str) -> List[str]: + lst: List[str] = [] + for x in range(256): + try: + lst += (bytes((x,)).decode(enc),) + except Exception: + lst += (chr(x),) + return lst + + +def rev_encoding(enc: List[str]) -> Dict[str, int]: + rev: Dict[str, int] = {} + for i in range(256): + char = enc[i] + if char == "\u0000": + continue + assert char not in rev, f"{char} at {i} already at {rev[char]}" + rev[char] = i + return rev + + +_win_encoding = fill_from_encoding("cp1252") +_mac_encoding = fill_from_encoding("mac_roman") + + +_win_encoding_rev: Dict[str, int] = rev_encoding(_win_encoding) +_mac_encoding_rev: Dict[str, int] = rev_encoding(_mac_encoding) +_symbol_encoding_rev: Dict[str, int] = rev_encoding(_symbol_encoding) +_zapfding_encoding_rev: Dict[str, int] = rev_encoding(_zapfding_encoding) +_pdfdoc_encoding_rev: Dict[str, int] = rev_encoding(_pdfdoc_encoding) + + +charset_encoding: Dict[str, List[str]] = { + "/StandardCoding": _std_encoding, + "/WinAnsiEncoding": _win_encoding, + "/MacRomanEncoding": _mac_encoding, + "/PDFDocEncoding": _pdfdoc_encoding, + "/Symbol": _symbol_encoding, + "/ZapfDingbats": _zapfding_encoding, +} + +__all__ = [ + "adobe_glyphs", + "_std_encoding", + "_symbol_encoding", + "_zapfding_encoding", + "_pdfdoc_encoding", + "_pdfdoc_encoding_rev", + "_win_encoding", + "_mac_encoding", + "charset_encoding", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..67763ab0 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/adobe_glyphs.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/adobe_glyphs.cpython-38.pyc new file mode 100644 index 00000000..c575d631 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/adobe_glyphs.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/pdfdoc.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/pdfdoc.cpython-38.pyc new file mode 100644 index 00000000..ba9d7938 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/pdfdoc.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/std.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/std.cpython-38.pyc new file mode 100644 index 00000000..ac4d7e8d Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/std.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/symbol.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/symbol.cpython-38.pyc new file mode 100644 index 00000000..ed9ef531 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/symbol.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/zapfding.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/zapfding.cpython-38.pyc new file mode 100644 index 00000000..98138724 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/__pycache__/zapfding.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/adobe_glyphs.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/adobe_glyphs.py new file mode 100644 index 00000000..19e2a99c --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/adobe_glyphs.py @@ -0,0 +1,13970 @@ +# https://raw.githubusercontent.com/adobe-type-tools/agl-aglfn/master/glyphlist.txt + +# converted manually to python +# Extended with data from GlyphNameFormatter: +# https://github.com/LettError/glyphNameFormatter + +# ----------------------------------------------------------- +# Copyright 2002-2019 Adobe (http://www.adobe.com/). +# +# Redistribution and use in source and binary forms, with or +# without modification, are permitted provided that the +# following conditions are met: +# +# Redistributions of source code must retain the above +# copyright notice, this list of conditions and the following +# disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials +# provided with the distribution. +# +# Neither the name of Adobe nor the names of its contributors +# may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------- +# Name: Adobe Glyph List +# Table version: 2.0 +# Date: September 20, 2002 +# URL: https://github.com/adobe-type-tools/agl-aglfn +# +# Format: two semicolon-delimited fields: +# (1) glyph name--upper/lowercase letters and digits +# (2) Unicode scalar value--four uppercase hexadecimal digits +# +adobe_glyphs = { + "/A": "\u0041", + "/AA": "\uA732", + "/AE": "\u00C6", + "/AEacute": "\u01FC", + "/AEmacron": "\u01E2", + "/AEsmall": "\uF7E6", + "/AO": "\uA734", + "/AU": "\uA736", + "/AV": "\uA738", + "/AVhorizontalbar": "\uA73A", + "/AY": "\uA73C", + "/Aacute": "\u00C1", + "/Aacutesmall": "\uF7E1", + "/Abreve": "\u0102", + "/Abreveacute": "\u1EAE", + "/Abrevecyr": "\u04D0", + "/Abrevecyrillic": "\u04D0", + "/Abrevedotbelow": "\u1EB6", + "/Abrevegrave": "\u1EB0", + "/Abrevehoi": "\u1EB2", + "/Abrevehookabove": "\u1EB2", + "/Abrevetilde": "\u1EB4", + "/Acaron": "\u01CD", + "/Acircle": "\u24B6", + "/Acircleblack": "\u1F150", + "/Acircumflex": "\u00C2", + "/Acircumflexacute": "\u1EA4", + "/Acircumflexdotbelow": "\u1EAC", + "/Acircumflexgrave": "\u1EA6", + "/Acircumflexhoi": "\u1EA8", + "/Acircumflexhookabove": "\u1EA8", + "/Acircumflexsmall": "\uF7E2", + "/Acircumflextilde": "\u1EAA", + "/Acute": "\uF6C9", + "/Acutesmall": "\uF7B4", + "/Acyr": "\u0410", + "/Acyrillic": "\u0410", + "/Adblgrave": "\u0200", + "/Adieresis": "\u00C4", + "/Adieresiscyr": "\u04D2", + "/Adieresiscyrillic": "\u04D2", + "/Adieresismacron": "\u01DE", + "/Adieresissmall": "\uF7E4", + "/Adot": "\u0226", + "/Adotbelow": "\u1EA0", + "/Adotmacron": "\u01E0", + "/Agrave": "\u00C0", + "/Agravedbl": "\u0200", + "/Agravesmall": "\uF7E0", + "/Ahoi": "\u1EA2", + "/Ahookabove": "\u1EA2", + "/Aiecyr": "\u04D4", + "/Aiecyrillic": "\u04D4", + "/Ainvertedbreve": "\u0202", + "/Akbar": "\uFDF3", + "/Alayhe": "\uFDF7", + "/Allah": "\uFDF2", + "/Alpha": "\u0391", + "/Alphaacute": "\u1FBB", + "/Alphaasper": "\u1F09", + "/Alphaasperacute": "\u1F0D", + "/Alphaasperacuteiotasub": "\u1F8D", + "/Alphaaspergrave": "\u1F0B", + "/Alphaaspergraveiotasub": "\u1F8B", + "/Alphaasperiotasub": "\u1F89", + "/Alphaaspertilde": "\u1F0F", + "/Alphaaspertildeiotasub": "\u1F8F", + "/Alphabreve": "\u1FB8", + "/Alphagrave": "\u1FBA", + "/Alphaiotasub": "\u1FBC", + "/Alphalenis": "\u1F08", + "/Alphalenisacute": "\u1F0C", + "/Alphalenisacuteiotasub": "\u1F8C", + "/Alphalenisgrave": "\u1F0A", + "/Alphalenisgraveiotasub": "\u1F8A", + "/Alphalenisiotasub": "\u1F88", + "/Alphalenistilde": "\u1F0E", + "/Alphalenistildeiotasub": "\u1F8E", + "/Alphatonos": "\u0386", + "/Alphawithmacron": "\u1FB9", + "/Amacron": "\u0100", + "/Amonospace": "\uFF21", + "/Aogonek": "\u0104", + "/Aparens": "\u1F110", + "/Aring": "\u00C5", + "/Aringacute": "\u01FA", + "/Aringbelow": "\u1E00", + "/Aringsmall": "\uF7E5", + "/Asmall": "\uF761", + "/Asquare": "\u1F130", + "/Asquareblack": "\u1F170", + "/Astroke": "\u023A", + "/Atilde": "\u00C3", + "/Atildesmall": "\uF7E3", + "/Aturned": "\u2C6F", + "/Ayahend": "\u06DD", + "/Aybarmenian": "\u0531", + "/B": "\u0042", + "/Bcircle": "\u24B7", + "/Bcircleblack": "\u1F151", + "/Bdot": "\u1E02", + "/Bdotaccent": "\u1E02", + "/Bdotbelow": "\u1E04", + "/Becyr": "\u0411", + "/Becyrillic": "\u0411", + "/Benarmenian": "\u0532", + "/Beta": "\u0392", + "/Bflourish": "\uA796", + "/Bhook": "\u0181", + "/BismillahArRahmanArRaheem": "\uFDFD", + "/Blinebelow": "\u1E06", + "/Bmonospace": "\uFF22", + "/Bparens": "\u1F111", + "/Brevesmall": "\uF6F4", + "/Bscript": "\u212C", + "/Bsmall": "\uF762", + "/Bsquare": "\u1F131", + "/Bsquareblack": "\u1F171", + "/Bstroke": "\u0243", + "/Btopbar": "\u0182", + "/C": "\u0043", + "/CDcircle": "\u1F12D", + "/Caarmenian": "\u053E", + "/Cacute": "\u0106", + "/Caron": "\uF6CA", + "/Caronsmall": "\uF6F5", + "/Cbar": "\uA792", + "/Ccaron": "\u010C", + "/Ccedilla": "\u00C7", + "/Ccedillaacute": "\u1E08", + "/Ccedillasmall": "\uF7E7", + "/Ccircle": "\u24B8", + "/Ccircleblack": "\u1F152", + "/Ccircumflex": "\u0108", + "/Cdblstruck": "\u2102", + "/Cdot": "\u010A", + "/Cdotaccent": "\u010A", + "/Cdotreversed": "\uA73E", + "/Cedillasmall": "\uF7B8", + "/Cfraktur": "\u212D", + "/Chaarmenian": "\u0549", + "/Cheabkhasiancyrillic": "\u04BC", + "/Cheabkhcyr": "\u04BC", + "/Cheabkhtailcyr": "\u04BE", + "/Checyr": "\u0427", + "/Checyrillic": "\u0427", + "/Chedescenderabkhasiancyrillic": "\u04BE", + "/Chedescendercyrillic": "\u04B6", + "/Chedieresiscyr": "\u04F4", + "/Chedieresiscyrillic": "\u04F4", + "/Cheharmenian": "\u0543", + "/Chekhakascyr": "\u04CB", + "/Chekhakassiancyrillic": "\u04CB", + "/Chetailcyr": "\u04B6", + "/Chevertcyr": "\u04B8", + "/Cheverticalstrokecyrillic": "\u04B8", + "/Chi": "\u03A7", + "/Chook": "\u0187", + "/Circumflexsmall": "\uF6F6", + "/Citaliccircle": "\u1F12B", + "/Cmonospace": "\uFF23", + "/Coarmenian": "\u0551", + "/Con": "\uA76E", + "/Cparens": "\u1F112", + "/Csmall": "\uF763", + "/Csquare": "\u1F132", + "/Csquareblack": "\u1F172", + "/Cstretched": "\u0297", + "/Cstroke": "\u023B", + "/Cuatrillo": "\uA72C", + "/Cuatrillocomma": "\uA72E", + "/D": "\u0044", + "/DZ": "\u01F1", + "/DZcaron": "\u01C4", + "/Daarmenian": "\u0534", + "/Dafrican": "\u0189", + "/Dcaron": "\u010E", + "/Dcedilla": "\u1E10", + "/Dchecyr": "\u052C", + "/Dcircle": "\u24B9", + "/Dcircleblack": "\u1F153", + "/Dcircumflexbelow": "\u1E12", + "/Dcroat": "\u0110", + "/Ddblstruckitalic": "\u2145", + "/Ddot": "\u1E0A", + "/Ddotaccent": "\u1E0A", + "/Ddotbelow": "\u1E0C", + "/Decyr": "\u0414", + "/Decyrillic": "\u0414", + "/Deicoptic": "\u03EE", + "/Dekomicyr": "\u0500", + "/Delta": "\u2206", + "/Deltagreek": "\u0394", + "/Dhook": "\u018A", + "/Dieresis": "\uF6CB", + "/DieresisAcute": "\uF6CC", + "/DieresisGrave": "\uF6CD", + "/Dieresissmall": "\uF7A8", + "/Digamma": "\u03DC", + "/Digammagreek": "\u03DC", + "/Digammapamphylian": "\u0376", + "/Dinsular": "\uA779", + "/Djecyr": "\u0402", + "/Djecyrillic": "\u0402", + "/Djekomicyr": "\u0502", + "/Dlinebelow": "\u1E0E", + "/Dmonospace": "\uFF24", + "/Dotaccentsmall": "\uF6F7", + "/Dparens": "\u1F113", + "/Dslash": "\u0110", + "/Dsmall": "\uF764", + "/Dsquare": "\u1F133", + "/Dsquareblack": "\u1F173", + "/Dtopbar": "\u018B", + "/Dz": "\u01F2", + "/Dzcaron": "\u01C5", + "/Dzeabkhasiancyrillic": "\u04E0", + "/Dzeabkhcyr": "\u04E0", + "/Dzecyr": "\u0405", + "/Dzecyrillic": "\u0405", + "/Dzhecyr": "\u040F", + "/Dzhecyrillic": "\u040F", + "/Dzjekomicyr": "\u0506", + "/Dzzhecyr": "\u052A", + "/E": "\u0045", + "/Eacute": "\u00C9", + "/Eacutesmall": "\uF7E9", + "/Ebreve": "\u0114", + "/Ecaron": "\u011A", + "/Ecedilla": "\u0228", + "/Ecedillabreve": "\u1E1C", + "/Echarmenian": "\u0535", + "/Ecircle": "\u24BA", + "/Ecircleblack": "\u1F154", + "/Ecircumflex": "\u00CA", + "/Ecircumflexacute": "\u1EBE", + "/Ecircumflexbelow": "\u1E18", + "/Ecircumflexdotbelow": "\u1EC6", + "/Ecircumflexgrave": "\u1EC0", + "/Ecircumflexhoi": "\u1EC2", + "/Ecircumflexhookabove": "\u1EC2", + "/Ecircumflexsmall": "\uF7EA", + "/Ecircumflextilde": "\u1EC4", + "/Ecyrillic": "\u0404", + "/Edblgrave": "\u0204", + "/Edieresis": "\u00CB", + "/Edieresissmall": "\uF7EB", + "/Edot": "\u0116", + "/Edotaccent": "\u0116", + "/Edotbelow": "\u1EB8", + "/Efcyr": "\u0424", + "/Efcyrillic": "\u0424", + "/Egrave": "\u00C8", + "/Egravedbl": "\u0204", + "/Egravesmall": "\uF7E8", + "/Egyptain": "\uA724", + "/Egyptalef": "\uA722", + "/Eharmenian": "\u0537", + "/Ehoi": "\u1EBA", + "/Ehookabove": "\u1EBA", + "/Eightroman": "\u2167", + "/Einvertedbreve": "\u0206", + "/Eiotifiedcyr": "\u0464", + "/Eiotifiedcyrillic": "\u0464", + "/Elcyr": "\u041B", + "/Elcyrillic": "\u041B", + "/Elevenroman": "\u216A", + "/Elhookcyr": "\u0512", + "/Elmiddlehookcyr": "\u0520", + "/Elsharptailcyr": "\u04C5", + "/Eltailcyr": "\u052E", + "/Emacron": "\u0112", + "/Emacronacute": "\u1E16", + "/Emacrongrave": "\u1E14", + "/Emcyr": "\u041C", + "/Emcyrillic": "\u041C", + "/Emonospace": "\uFF25", + "/Emsharptailcyr": "\u04CD", + "/Encyr": "\u041D", + "/Encyrillic": "\u041D", + "/Endescendercyrillic": "\u04A2", + "/Eng": "\u014A", + "/Engecyr": "\u04A4", + "/Enghecyrillic": "\u04A4", + "/Enhookcyr": "\u04C7", + "/Enhookcyrillic": "\u04C7", + "/Enhookleftcyr": "\u0528", + "/Enmiddlehookcyr": "\u0522", + "/Ensharptailcyr": "\u04C9", + "/Entailcyr": "\u04A2", + "/Eogonek": "\u0118", + "/Eopen": "\u0190", + "/Eparens": "\u1F114", + "/Epsilon": "\u0395", + "/Epsilonacute": "\u1FC9", + "/Epsilonasper": "\u1F19", + "/Epsilonasperacute": "\u1F1D", + "/Epsilonaspergrave": "\u1F1B", + "/Epsilongrave": "\u1FC8", + "/Epsilonlenis": "\u1F18", + "/Epsilonlenisacute": "\u1F1C", + "/Epsilonlenisgrave": "\u1F1A", + "/Epsilontonos": "\u0388", + "/Ercyr": "\u0420", + "/Ercyrillic": "\u0420", + "/Ereversed": "\u018E", + "/Ereversedcyr": "\u042D", + "/Ereversedcyrillic": "\u042D", + "/Ereverseddieresiscyr": "\u04EC", + "/Ereversedopen": "\uA7AB", + "/Ertickcyr": "\u048E", + "/Escript": "\u2130", + "/Escyr": "\u0421", + "/Escyrillic": "\u0421", + "/Esdescendercyrillic": "\u04AA", + "/Esh": "\u01A9", + "/Esmall": "\uF765", + "/Esmallturned": "\u2C7B", + "/Esquare": "\u1F134", + "/Esquareblack": "\u1F174", + "/Estailcyr": "\u04AA", + "/Estroke": "\u0246", + "/Et": "\uA76A", + "/Eta": "\u0397", + "/Etaacute": "\u1FCB", + "/Etaasper": "\u1F29", + "/Etaasperacute": "\u1F2D", + "/Etaasperacuteiotasub": "\u1F9D", + "/Etaaspergrave": "\u1F2B", + "/Etaaspergraveiotasub": "\u1F9B", + "/Etaasperiotasub": "\u1F99", + "/Etaaspertilde": "\u1F2F", + "/Etaaspertildeiotasub": "\u1F9F", + "/Etagrave": "\u1FCA", + "/Etaiotasub": "\u1FCC", + "/Etalenis": "\u1F28", + "/Etalenisacute": "\u1F2C", + "/Etalenisacuteiotasub": "\u1F9C", + "/Etalenisgrave": "\u1F2A", + "/Etalenisgraveiotasub": "\u1F9A", + "/Etalenisiotasub": "\u1F98", + "/Etalenistilde": "\u1F2E", + "/Etalenistildeiotasub": "\u1F9E", + "/Etarmenian": "\u0538", + "/Etatonos": "\u0389", + "/Eth": "\u00D0", + "/Ethsmall": "\uF7F0", + "/Etilde": "\u1EBC", + "/Etildebelow": "\u1E1A", + "/Eukrcyr": "\u0404", + "/Euro": "\u20AC", + "/Ezh": "\u01B7", + "/Ezhcaron": "\u01EE", + "/Ezhreversed": "\u01B8", + "/F": "\u0046", + "/Fcircle": "\u24BB", + "/Fcircleblack": "\u1F155", + "/Fdot": "\u1E1E", + "/Fdotaccent": "\u1E1E", + "/Feharmenian": "\u0556", + "/Feicoptic": "\u03E4", + "/Fhook": "\u0191", + "/Finsular": "\uA77B", + "/Fitacyr": "\u0472", + "/Fitacyrillic": "\u0472", + "/Fiveroman": "\u2164", + "/Fmonospace": "\uFF26", + "/Fourroman": "\u2163", + "/Fparens": "\u1F115", + "/Fscript": "\u2131", + "/Fsmall": "\uF766", + "/Fsquare": "\u1F135", + "/Fsquareblack": "\u1F175", + "/Fstroke": "\uA798", + "/Fturned": "\u2132", + "/G": "\u0047", + "/GBsquare": "\u3387", + "/Gacute": "\u01F4", + "/Gamma": "\u0393", + "/Gammaafrican": "\u0194", + "/Gammadblstruck": "\u213E", + "/Gangiacoptic": "\u03EA", + "/Gbreve": "\u011E", + "/Gcaron": "\u01E6", + "/Gcedilla": "\u0122", + "/Gcircle": "\u24BC", + "/Gcircleblack": "\u1F156", + "/Gcircumflex": "\u011C", + "/Gcommaaccent": "\u0122", + "/Gdot": "\u0120", + "/Gdotaccent": "\u0120", + "/Gecyr": "\u0413", + "/Gecyrillic": "\u0413", + "/Gehookcyr": "\u0494", + "/Gehookstrokecyr": "\u04FA", + "/Germandbls": "\u1E9E", + "/Gestrokecyr": "\u0492", + "/Getailcyr": "\u04F6", + "/Geupcyr": "\u0490", + "/Ghadarmenian": "\u0542", + "/Ghemiddlehookcyrillic": "\u0494", + "/Ghestrokecyrillic": "\u0492", + "/Gheupturncyrillic": "\u0490", + "/Ghook": "\u0193", + "/Ghooksmall": "\u029B", + "/Gimarmenian": "\u0533", + "/Ginsular": "\uA77D", + "/Ginsularturned": "\uA77E", + "/Gjecyr": "\u0403", + "/Gjecyrillic": "\u0403", + "/Glottalstop": "\u0241", + "/Gmacron": "\u1E20", + "/Gmonospace": "\uFF27", + "/Gobliquestroke": "\uA7A0", + "/Gparens": "\u1F116", + "/Grave": "\uF6CE", + "/Gravesmall": "\uF760", + "/Gsmall": "\uF767", + "/Gsmallhook": "\u029B", + "/Gsquare": "\u1F136", + "/Gsquareblack": "\u1F176", + "/Gstroke": "\u01E4", + "/Gturnedsans": "\u2141", + "/H": "\u0048", + "/H18533": "\u25CF", + "/H18543": "\u25AA", + "/H18551": "\u25AB", + "/H22073": "\u25A1", + "/HPsquare": "\u33CB", + "/HVsquare": "\u1F14A", + "/Haabkhasiancyrillic": "\u04A8", + "/Haabkhcyr": "\u04A8", + "/Hacyr": "\u0425", + "/Hadescendercyrillic": "\u04B2", + "/Hahookcyr": "\u04FC", + "/Hardcyr": "\u042A", + "/Hardsigncyrillic": "\u042A", + "/Hastrokecyr": "\u04FE", + "/Hbar": "\u0126", + "/Hbrevebelow": "\u1E2A", + "/Hcaron": "\u021E", + "/Hcedilla": "\u1E28", + "/Hcircle": "\u24BD", + "/Hcircleblack": "\u1F157", + "/Hcircumflex": "\u0124", + "/Hdblstruck": "\u210D", + "/Hdescender": "\u2C67", + "/Hdieresis": "\u1E26", + "/Hdot": "\u1E22", + "/Hdotaccent": "\u1E22", + "/Hdotbelow": "\u1E24", + "/Heng": "\uA726", + "/Heta": "\u0370", + "/Hfraktur": "\u210C", + "/Hgfullwidth": "\u32CC", + "/Hhalf": "\u2C75", + "/Hhook": "\uA7AA", + "/Hmonospace": "\uFF28", + "/Hoarmenian": "\u0540", + "/HonAA": "\u0611", + "/HonRA": "\u0612", + "/HonSAW": "\u0610", + "/Horicoptic": "\u03E8", + "/Hparens": "\u1F117", + "/Hscript": "\u210B", + "/Hsmall": "\uF768", + "/Hsquare": "\u1F137", + "/Hsquareblack": "\u1F177", + "/Hstrokemod": "\uA7F8", + "/Hturned": "\uA78D", + "/Hungarumlaut": "\uF6CF", + "/Hungarumlautsmall": "\uF6F8", + "/Hwair": "\u01F6", + "/Hzsquare": "\u3390", + "/I": "\u0049", + "/IAcyrillic": "\u042F", + "/ICsquareblack": "\u1F18B", + "/IJ": "\u0132", + "/IUcyrillic": "\u042E", + "/Iacute": "\u00CD", + "/Iacutesmall": "\uF7ED", + "/Ibreve": "\u012C", + "/Icaron": "\u01CF", + "/Icircle": "\u24BE", + "/Icircleblack": "\u1F158", + "/Icircumflex": "\u00CE", + "/Icircumflexsmall": "\uF7EE", + "/Icyr": "\u0418", + "/Icyrillic": "\u0406", + "/Idblgrave": "\u0208", + "/Idieresis": "\u00CF", + "/Idieresisacute": "\u1E2E", + "/Idieresiscyr": "\u04E4", + "/Idieresiscyrillic": "\u04E4", + "/Idieresissmall": "\uF7EF", + "/Idot": "\u0130", + "/Idotaccent": "\u0130", + "/Idotbelow": "\u1ECA", + "/Iebrevecyr": "\u04D6", + "/Iebrevecyrillic": "\u04D6", + "/Iecyr": "\u0415", + "/Iecyrillic": "\u0415", + "/Iegravecyr": "\u0400", + "/Ifraktur": "\u2111", + "/Igrave": "\u00CC", + "/Igravecyr": "\u040D", + "/Igravedbl": "\u0208", + "/Igravesmall": "\uF7EC", + "/Ihoi": "\u1EC8", + "/Ihookabove": "\u1EC8", + "/Iicyrillic": "\u0418", + "/Iinvertedbreve": "\u020A", + "/Iishortcyrillic": "\u0419", + "/Imacron": "\u012A", + "/Imacroncyr": "\u04E2", + "/Imacroncyrillic": "\u04E2", + "/Imonospace": "\uFF29", + "/Iniarmenian": "\u053B", + "/Iocyr": "\u0401", + "/Iocyrillic": "\u0401", + "/Iogonek": "\u012E", + "/Iota": "\u0399", + "/Iotaacute": "\u1FDB", + "/Iotaafrican": "\u0196", + "/Iotaasper": "\u1F39", + "/Iotaasperacute": "\u1F3D", + "/Iotaaspergrave": "\u1F3B", + "/Iotaaspertilde": "\u1F3F", + "/Iotabreve": "\u1FD8", + "/Iotadieresis": "\u03AA", + "/Iotagrave": "\u1FDA", + "/Iotalenis": "\u1F38", + "/Iotalenisacute": "\u1F3C", + "/Iotalenisgrave": "\u1F3A", + "/Iotalenistilde": "\u1F3E", + "/Iotatonos": "\u038A", + "/Iotawithmacron": "\u1FD9", + "/Iparens": "\u1F118", + "/Is": "\uA76C", + "/Iscript": "\u2110", + "/Ishortcyr": "\u0419", + "/Ishortsharptailcyr": "\u048A", + "/Ismall": "\uF769", + "/Isquare": "\u1F138", + "/Isquareblack": "\u1F178", + "/Istroke": "\u0197", + "/Itilde": "\u0128", + "/Itildebelow": "\u1E2C", + "/Iukrcyr": "\u0406", + "/Izhitsacyr": "\u0474", + "/Izhitsacyrillic": "\u0474", + "/Izhitsadblgravecyrillic": "\u0476", + "/Izhitsagravedblcyr": "\u0476", + "/J": "\u004A", + "/Jaarmenian": "\u0541", + "/Jallajalalouhou": "\uFDFB", + "/Jcircle": "\u24BF", + "/Jcircleblack": "\u1F159", + "/Jcircumflex": "\u0134", + "/Jcrossed-tail": "\uA7B2", + "/Jecyr": "\u0408", + "/Jecyrillic": "\u0408", + "/Jheharmenian": "\u054B", + "/Jmonospace": "\uFF2A", + "/Jparens": "\u1F119", + "/Jsmall": "\uF76A", + "/Jsquare": "\u1F139", + "/Jsquareblack": "\u1F179", + "/Jstroke": "\u0248", + "/K": "\u004B", + "/KBsquare": "\u3385", + "/KKsquare": "\u33CD", + "/KORONIS": "\u1FBD", + "/Kaaleutcyr": "\u051E", + "/Kabashkcyr": "\u04A0", + "/Kabashkircyrillic": "\u04A0", + "/Kacute": "\u1E30", + "/Kacyr": "\u041A", + "/Kacyrillic": "\u041A", + "/Kadescendercyrillic": "\u049A", + "/Kahookcyr": "\u04C3", + "/Kahookcyrillic": "\u04C3", + "/Kaisymbol": "\u03CF", + "/Kappa": "\u039A", + "/Kastrokecyr": "\u049E", + "/Kastrokecyrillic": "\u049E", + "/Katailcyr": "\u049A", + "/Kaverticalstrokecyr": "\u049C", + "/Kaverticalstrokecyrillic": "\u049C", + "/Kcaron": "\u01E8", + "/Kcedilla": "\u0136", + "/Kcircle": "\u24C0", + "/Kcircleblack": "\u1F15A", + "/Kcommaaccent": "\u0136", + "/Kdescender": "\u2C69", + "/Kdiagonalstroke": "\uA742", + "/Kdotbelow": "\u1E32", + "/Keharmenian": "\u0554", + "/Kenarmenian": "\u053F", + "/Khacyrillic": "\u0425", + "/Kheicoptic": "\u03E6", + "/Khook": "\u0198", + "/Kjecyr": "\u040C", + "/Kjecyrillic": "\u040C", + "/Klinebelow": "\u1E34", + "/Kmonospace": "\uFF2B", + "/Kobliquestroke": "\uA7A2", + "/Koppa": "\u03DE", + "/Koppaarchaic": "\u03D8", + "/Koppacyr": "\u0480", + "/Koppacyrillic": "\u0480", + "/Koppagreek": "\u03DE", + "/Kparens": "\u1F11A", + "/Ksicyr": "\u046E", + "/Ksicyrillic": "\u046E", + "/Ksmall": "\uF76B", + "/Ksquare": "\u1F13A", + "/Ksquareblack": "\u1F17A", + "/Kstroke": "\uA740", + "/Kstrokediagonalstroke": "\uA744", + "/Kturned": "\uA7B0", + "/L": "\u004C", + "/LJ": "\u01C7", + "/LL": "\uF6BF", + "/LLwelsh": "\u1EFA", + "/LTDfullwidth": "\u32CF", + "/Lacute": "\u0139", + "/Lambda": "\u039B", + "/Lbar": "\u023D", + "/Lbelt": "\uA7AD", + "/Lbroken": "\uA746", + "/Lcaron": "\u013D", + "/Lcedilla": "\u013B", + "/Lcircle": "\u24C1", + "/Lcircleblack": "\u1F15B", + "/Lcircumflexbelow": "\u1E3C", + "/Lcommaaccent": "\u013B", + "/Ldblbar": "\u2C60", + "/Ldot": "\u013F", + "/Ldotaccent": "\u013F", + "/Ldotbelow": "\u1E36", + "/Ldotbelowmacron": "\u1E38", + "/Lhacyr": "\u0514", + "/Liwnarmenian": "\u053C", + "/Lj": "\u01C8", + "/Ljecyr": "\u0409", + "/Ljecyrillic": "\u0409", + "/Ljekomicyr": "\u0508", + "/Llinebelow": "\u1E3A", + "/Lmacrondot": "\u1E38", + "/Lmiddletilde": "\u2C62", + "/Lmonospace": "\uFF2C", + "/Lparens": "\u1F11B", + "/Lreversedsans": "\u2143", + "/Lscript": "\u2112", + "/Lslash": "\u0141", + "/Lslashsmall": "\uF6F9", + "/Lsmall": "\uF76C", + "/Lsquare": "\u1F13B", + "/Lsquareblack": "\u1F17B", + "/Lstroke": "\uA748", + "/Lturned": "\uA780", + "/Lturnedsans": "\u2142", + "/M": "\u004D", + "/MBsquare": "\u3386", + "/MVsquare": "\u1F14B", + "/Macron": "\uF6D0", + "/Macronsmall": "\uF7AF", + "/Macute": "\u1E3E", + "/Mcircle": "\u24C2", + "/Mcircleblack": "\u1F15C", + "/Mdot": "\u1E40", + "/Mdotaccent": "\u1E40", + "/Mdotbelow": "\u1E42", + "/Menarmenian": "\u0544", + "/Mhook": "\u2C6E", + "/Mmonospace": "\uFF2D", + "/Mohammad": "\uFDF4", + "/Mparens": "\u1F11C", + "/Mscript": "\u2133", + "/Msmall": "\uF76D", + "/Msquare": "\u1F13C", + "/Msquareblack": "\u1F17C", + "/Mturned": "\u019C", + "/Mturnedsmall": "\uA7FA", + "/Mu": "\u039C", + "/N": "\u004E", + "/NJ": "\u01CA", + "/Nacute": "\u0143", + "/Ncaron": "\u0147", + "/Ncedilla": "\u0145", + "/Ncircle": "\u24C3", + "/Ncircleblack": "\u1F15D", + "/Ncircumflexbelow": "\u1E4A", + "/Ncommaaccent": "\u0145", + "/Ndblstruck": "\u2115", + "/Ndescender": "\uA790", + "/Ndot": "\u1E44", + "/Ndotaccent": "\u1E44", + "/Ndotbelow": "\u1E46", + "/Ngrave": "\u01F8", + "/Nhookleft": "\u019D", + "/Nineroman": "\u2168", + "/Nj": "\u01CB", + "/Njecyr": "\u040A", + "/Njecyrillic": "\u040A", + "/Njekomicyr": "\u050A", + "/Nlinebelow": "\u1E48", + "/Nlongrightleg": "\u0220", + "/Nmonospace": "\uFF2E", + "/Nobliquestroke": "\uA7A4", + "/Nowarmenian": "\u0546", + "/Nparens": "\u1F11D", + "/Nsmall": "\uF76E", + "/Nsquare": "\u1F13D", + "/Nsquareblack": "\u1F17D", + "/Ntilde": "\u00D1", + "/Ntildesmall": "\uF7F1", + "/Nu": "\u039D", + "/O": "\u004F", + "/OE": "\u0152", + "/OEsmall": "\uF6FA", + "/OO": "\uA74E", + "/Oacute": "\u00D3", + "/Oacutesmall": "\uF7F3", + "/Obar": "\u019F", + "/Obarcyr": "\u04E8", + "/Obardieresiscyr": "\u04EA", + "/Obarredcyrillic": "\u04E8", + "/Obarreddieresiscyrillic": "\u04EA", + "/Obreve": "\u014E", + "/Ocaron": "\u01D1", + "/Ocenteredtilde": "\u019F", + "/Ocircle": "\u24C4", + "/Ocircleblack": "\u1F15E", + "/Ocircumflex": "\u00D4", + "/Ocircumflexacute": "\u1ED0", + "/Ocircumflexdotbelow": "\u1ED8", + "/Ocircumflexgrave": "\u1ED2", + "/Ocircumflexhoi": "\u1ED4", + "/Ocircumflexhookabove": "\u1ED4", + "/Ocircumflexsmall": "\uF7F4", + "/Ocircumflextilde": "\u1ED6", + "/Ocyr": "\u041E", + "/Ocyrillic": "\u041E", + "/Odblacute": "\u0150", + "/Odblgrave": "\u020C", + "/Odieresis": "\u00D6", + "/Odieresiscyr": "\u04E6", + "/Odieresiscyrillic": "\u04E6", + "/Odieresismacron": "\u022A", + "/Odieresissmall": "\uF7F6", + "/Odot": "\u022E", + "/Odotbelow": "\u1ECC", + "/Odotmacron": "\u0230", + "/Ogoneksmall": "\uF6FB", + "/Ograve": "\u00D2", + "/Ogravedbl": "\u020C", + "/Ogravesmall": "\uF7F2", + "/Oharmenian": "\u0555", + "/Ohm": "\u2126", + "/Ohoi": "\u1ECE", + "/Ohookabove": "\u1ECE", + "/Ohorn": "\u01A0", + "/Ohornacute": "\u1EDA", + "/Ohorndotbelow": "\u1EE2", + "/Ohorngrave": "\u1EDC", + "/Ohornhoi": "\u1EDE", + "/Ohornhookabove": "\u1EDE", + "/Ohorntilde": "\u1EE0", + "/Ohungarumlaut": "\u0150", + "/Oi": "\u01A2", + "/Oinvertedbreve": "\u020E", + "/Oloop": "\uA74C", + "/Omacron": "\u014C", + "/Omacronacute": "\u1E52", + "/Omacrongrave": "\u1E50", + "/Omega": "\u2126", + "/Omegaacute": "\u1FFB", + "/Omegaasper": "\u1F69", + "/Omegaasperacute": "\u1F6D", + "/Omegaasperacuteiotasub": "\u1FAD", + "/Omegaaspergrave": "\u1F6B", + "/Omegaaspergraveiotasub": "\u1FAB", + "/Omegaasperiotasub": "\u1FA9", + "/Omegaaspertilde": "\u1F6F", + "/Omegaaspertildeiotasub": "\u1FAF", + "/Omegacyr": "\u0460", + "/Omegacyrillic": "\u0460", + "/Omegagrave": "\u1FFA", + "/Omegagreek": "\u03A9", + "/Omegaiotasub": "\u1FFC", + "/Omegalenis": "\u1F68", + "/Omegalenisacute": "\u1F6C", + "/Omegalenisacuteiotasub": "\u1FAC", + "/Omegalenisgrave": "\u1F6A", + "/Omegalenisgraveiotasub": "\u1FAA", + "/Omegalenisiotasub": "\u1FA8", + "/Omegalenistilde": "\u1F6E", + "/Omegalenistildeiotasub": "\u1FAE", + "/Omegaroundcyr": "\u047A", + "/Omegaroundcyrillic": "\u047A", + "/Omegatitlocyr": "\u047C", + "/Omegatitlocyrillic": "\u047C", + "/Omegatonos": "\u038F", + "/Omicron": "\u039F", + "/Omicronacute": "\u1FF9", + "/Omicronasper": "\u1F49", + "/Omicronasperacute": "\u1F4D", + "/Omicronaspergrave": "\u1F4B", + "/Omicrongrave": "\u1FF8", + "/Omicronlenis": "\u1F48", + "/Omicronlenisacute": "\u1F4C", + "/Omicronlenisgrave": "\u1F4A", + "/Omicrontonos": "\u038C", + "/Omonospace": "\uFF2F", + "/Oneroman": "\u2160", + "/Oogonek": "\u01EA", + "/Oogonekmacron": "\u01EC", + "/Oopen": "\u0186", + "/Oparens": "\u1F11E", + "/Oslash": "\u00D8", + "/Oslashacute": "\u01FE", + "/Oslashsmall": "\uF7F8", + "/Osmall": "\uF76F", + "/Osquare": "\u1F13E", + "/Osquareblack": "\u1F17E", + "/Ostroke": "\uA74A", + "/Ostrokeacute": "\u01FE", + "/Otcyr": "\u047E", + "/Otcyrillic": "\u047E", + "/Otilde": "\u00D5", + "/Otildeacute": "\u1E4C", + "/Otildedieresis": "\u1E4E", + "/Otildemacron": "\u022C", + "/Otildesmall": "\uF7F5", + "/Ou": "\u0222", + "/P": "\u0050", + "/PAsquareblack": "\u1F18C", + "/PPVsquare": "\u1F14E", + "/Pacute": "\u1E54", + "/Palochkacyr": "\u04C0", + "/Pcircle": "\u24C5", + "/Pcircleblack": "\u1F15F", + "/Pcrosssquareblack": "\u1F18A", + "/Pdblstruck": "\u2119", + "/Pdot": "\u1E56", + "/Pdotaccent": "\u1E56", + "/Pecyr": "\u041F", + "/Pecyrillic": "\u041F", + "/Peharmenian": "\u054A", + "/Pehookcyr": "\u04A6", + "/Pemiddlehookcyrillic": "\u04A6", + "/Petailcyr": "\u0524", + "/Pflourish": "\uA752", + "/Phi": "\u03A6", + "/Phook": "\u01A4", + "/Pi": "\u03A0", + "/Pidblstruck": "\u213F", + "/Piwrarmenian": "\u0553", + "/Pmonospace": "\uFF30", + "/Pparens": "\u1F11F", + "/Psi": "\u03A8", + "/Psicyr": "\u0470", + "/Psicyrillic": "\u0470", + "/Psmall": "\uF770", + "/Psquare": "\u1F13F", + "/Psquareblack": "\u1F17F", + "/Pstroke": "\u2C63", + "/Pstrokedescender": "\uA750", + "/Ptail": "\uA754", + "/Q": "\u0051", + "/Qacyr": "\u051A", + "/QalaUsedAsKoranicStopSign": "\uFDF1", + "/Qcircle": "\u24C6", + "/Qcircleblack": "\u1F160", + "/Qdblstruck": "\u211A", + "/Qdiagonalstroke": "\uA758", + "/Qmonospace": "\uFF31", + "/Qparens": "\u1F120", + "/Qrotated": "\u213A", + "/Qsmall": "\uF771", + "/Qsmallhooktail": "\u024A", + "/Qsquare": "\u1F140", + "/Qsquareblack": "\u1F180", + "/Qstrokedescender": "\uA756", + "/R": "\u0052", + "/Raarmenian": "\u054C", + "/Racute": "\u0154", + "/Rasoul": "\uFDF6", + "/Rcaron": "\u0158", + "/Rcedilla": "\u0156", + "/Rcircle": "\u24C7", + "/Rcircleblack": "\u1F161", + "/Rcommaaccent": "\u0156", + "/Rdblgrave": "\u0210", + "/Rdblstruck": "\u211D", + "/Rdot": "\u1E58", + "/Rdotaccent": "\u1E58", + "/Rdotbelow": "\u1E5A", + "/Rdotbelowmacron": "\u1E5C", + "/Reharmenian": "\u0550", + "/Reverseddottedsigmalunatesymbol": "\u03FF", + "/Reversedzecyr": "\u0510", + "/Rfraktur": "\u211C", + "/Rgravedbl": "\u0210", + "/Rhacyr": "\u0516", + "/Rho": "\u03A1", + "/Rhoasper": "\u1FEC", + "/Ringsmall": "\uF6FC", + "/Rinsular": "\uA782", + "/Rinvertedbreve": "\u0212", + "/Rinvertedsmall": "\u0281", + "/Ritaliccircle": "\u1F12C", + "/Rlinebelow": "\u1E5E", + "/Rmacrondot": "\u1E5C", + "/Rmonospace": "\uFF32", + "/Robliquestroke": "\uA7A6", + "/Rparens": "\u1F121", + "/Rrotunda": "\uA75A", + "/Rscript": "\u211B", + "/Rsmall": "\uF772", + "/Rsmallinverted": "\u0281", + "/Rsmallinvertedsuperior": "\u02B6", + "/Rsquare": "\u1F141", + "/Rsquareblack": "\u1F181", + "/Rstroke": "\u024C", + "/Rsupinvertedmod": "\u02B6", + "/Rtail": "\u2C64", + "/RubElHizbstart": "\u06DE", + "/Rumrotunda": "\uA75C", + "/Rumsmall": "\uA776", + "/S": "\u0053", + "/SAsquareblack": "\u1F18D", + "/SDsquare": "\u1F14C", + "/SF010000": "\u250C", + "/SF020000": "\u2514", + "/SF030000": "\u2510", + "/SF040000": "\u2518", + "/SF050000": "\u253C", + "/SF060000": "\u252C", + "/SF070000": "\u2534", + "/SF080000": "\u251C", + "/SF090000": "\u2524", + "/SF100000": "\u2500", + "/SF110000": "\u2502", + "/SF190000": "\u2561", + "/SF200000": "\u2562", + "/SF210000": "\u2556", + "/SF220000": "\u2555", + "/SF230000": "\u2563", + "/SF240000": "\u2551", + "/SF250000": "\u2557", + "/SF260000": "\u255D", + "/SF270000": "\u255C", + "/SF280000": "\u255B", + "/SF360000": "\u255E", + "/SF370000": "\u255F", + "/SF380000": "\u255A", + "/SF390000": "\u2554", + "/SF400000": "\u2569", + "/SF410000": "\u2566", + "/SF420000": "\u2560", + "/SF430000": "\u2550", + "/SF440000": "\u256C", + "/SF450000": "\u2567", + "/SF460000": "\u2568", + "/SF470000": "\u2564", + "/SF480000": "\u2565", + "/SF490000": "\u2559", + "/SF500000": "\u2558", + "/SF510000": "\u2552", + "/SF520000": "\u2553", + "/SF530000": "\u256B", + "/SF540000": "\u256A", + "/SSsquare": "\u1F14D", + "/Sacute": "\u015A", + "/Sacutedotaccent": "\u1E64", + "/Safha": "\u0603", + "/Sajdah": "\u06E9", + "/Salam": "\uFDF5", + "/Salla": "\uFDF9", + "/SallaUsedAsKoranicStopSign": "\uFDF0", + "/SallallahouAlayheWasallam": "\uFDFA", + "/Saltillo": "\uA78B", + "/Sampi": "\u03E0", + "/Sampiarchaic": "\u0372", + "/Sampigreek": "\u03E0", + "/San": "\u03FA", + "/Sanah": "\u0601", + "/Scaron": "\u0160", + "/Scarondot": "\u1E66", + "/Scarondotaccent": "\u1E66", + "/Scaronsmall": "\uF6FD", + "/Scedilla": "\u015E", + "/Schwa": "\u018F", + "/Schwacyr": "\u04D8", + "/Schwacyrillic": "\u04D8", + "/Schwadieresiscyr": "\u04DA", + "/Schwadieresiscyrillic": "\u04DA", + "/Scircle": "\u24C8", + "/Scircleblack": "\u1F162", + "/Scircumflex": "\u015C", + "/Scommaaccent": "\u0218", + "/Scriptg": "\uA7AC", + "/Sdot": "\u1E60", + "/Sdotaccent": "\u1E60", + "/Sdotbelow": "\u1E62", + "/Sdotbelowdotabove": "\u1E68", + "/Sdotbelowdotaccent": "\u1E68", + "/Seharmenian": "\u054D", + "/Semisoftcyr": "\u048C", + "/Sevenroman": "\u2166", + "/Shaarmenian": "\u0547", + "/Shacyr": "\u0428", + "/Shacyrillic": "\u0428", + "/Shchacyr": "\u0429", + "/Shchacyrillic": "\u0429", + "/Sheicoptic": "\u03E2", + "/SheneGerishin:hb": "\u059E", + "/Shhacyr": "\u04BA", + "/Shhacyrillic": "\u04BA", + "/Shhatailcyr": "\u0526", + "/Shimacoptic": "\u03EC", + "/Sho": "\u03F7", + "/Sigma": "\u03A3", + "/Sigmalunatesymbol": "\u03F9", + "/Sigmalunatesymboldotted": "\u03FE", + "/Sigmareversedlunatesymbol": "\u03FD", + "/Sinsular": "\uA784", + "/Sixroman": "\u2165", + "/Sjekomicyr": "\u050C", + "/Smonospace": "\uFF33", + "/Sobliquestroke": "\uA7A8", + "/Softcyr": "\u042C", + "/Softsigncyrillic": "\u042C", + "/Sparens": "\u1F122", + "/Sshell": "\u1F12A", + "/Ssmall": "\uF773", + "/Ssquare": "\u1F142", + "/Ssquareblack": "\u1F182", + "/Sswashtail": "\u2C7E", + "/Stigma": "\u03DA", + "/Stigmagreek": "\u03DA", + "/T": "\u0054", + "/Tau": "\u03A4", + "/Tbar": "\u0166", + "/Tcaron": "\u0164", + "/Tcedilla": "\u0162", + "/Tcircle": "\u24C9", + "/Tcircleblack": "\u1F163", + "/Tcircumflexbelow": "\u1E70", + "/Tcommaaccent": "\u0162", + "/Tdot": "\u1E6A", + "/Tdotaccent": "\u1E6A", + "/Tdotbelow": "\u1E6C", + "/Tecyr": "\u0422", + "/Tecyrillic": "\u0422", + "/Tedescendercyrillic": "\u04AC", + "/Tenroman": "\u2169", + "/Tetailcyr": "\u04AC", + "/Tetsecyr": "\u04B4", + "/Tetsecyrillic": "\u04B4", + "/Theta": "\u0398", + "/Thetasymbol": "\u03F4", + "/Thook": "\u01AC", + "/Thorn": "\u00DE", + "/Thornsmall": "\uF7FE", + "/Thornstroke": "\uA764", + "/Thornstrokedescender": "\uA766", + "/Threeroman": "\u2162", + "/Tildesmall": "\uF6FE", + "/Tinsular": "\uA786", + "/Tiwnarmenian": "\u054F", + "/Tjekomicyr": "\u050E", + "/Tlinebelow": "\u1E6E", + "/Tmonospace": "\uFF34", + "/Toarmenian": "\u0539", + "/Tonefive": "\u01BC", + "/Tonesix": "\u0184", + "/Tonetwo": "\u01A7", + "/Tparens": "\u1F123", + "/Tresillo": "\uA72A", + "/Tretroflexhook": "\u01AE", + "/Tsecyr": "\u0426", + "/Tsecyrillic": "\u0426", + "/Tshecyr": "\u040B", + "/Tshecyrillic": "\u040B", + "/Tsmall": "\uF774", + "/Tsquare": "\u1F143", + "/Tsquareblack": "\u1F183", + "/Tturned": "\uA7B1", + "/Twelveroman": "\u216B", + "/Twithdiagonalstroke": "\u023E", + "/Tworoman": "\u2161", + "/Tz": "\uA728", + "/U": "\u0055", + "/Uacute": "\u00DA", + "/Uacutedblcyr": "\u04F2", + "/Uacutesmall": "\uF7FA", + "/Ubar": "\u0244", + "/Ubreve": "\u016C", + "/Ucaron": "\u01D3", + "/Ucircle": "\u24CA", + "/Ucircleblack": "\u1F164", + "/Ucircumflex": "\u00DB", + "/Ucircumflexbelow": "\u1E76", + "/Ucircumflexsmall": "\uF7FB", + "/Ucyr": "\u0423", + "/Ucyrillic": "\u0423", + "/Udblacute": "\u0170", + "/Udblgrave": "\u0214", + "/Udieresis": "\u00DC", + "/Udieresisacute": "\u01D7", + "/Udieresisbelow": "\u1E72", + "/Udieresiscaron": "\u01D9", + "/Udieresiscyr": "\u04F0", + "/Udieresiscyrillic": "\u04F0", + "/Udieresisgrave": "\u01DB", + "/Udieresismacron": "\u01D5", + "/Udieresissmall": "\uF7FC", + "/Udotbelow": "\u1EE4", + "/Ugrave": "\u00D9", + "/Ugravedbl": "\u0214", + "/Ugravesmall": "\uF7F9", + "/Uhoi": "\u1EE6", + "/Uhookabove": "\u1EE6", + "/Uhorn": "\u01AF", + "/Uhornacute": "\u1EE8", + "/Uhorndotbelow": "\u1EF0", + "/Uhorngrave": "\u1EEA", + "/Uhornhoi": "\u1EEC", + "/Uhornhookabove": "\u1EEC", + "/Uhorntilde": "\u1EEE", + "/Uhungarumlaut": "\u0170", + "/Uhungarumlautcyrillic": "\u04F2", + "/Uinvertedbreve": "\u0216", + "/Ukcyr": "\u0478", + "/Ukcyrillic": "\u0478", + "/Umacron": "\u016A", + "/Umacroncyr": "\u04EE", + "/Umacroncyrillic": "\u04EE", + "/Umacrondieresis": "\u1E7A", + "/Umonospace": "\uFF35", + "/Uogonek": "\u0172", + "/Uparens": "\u1F124", + "/Upsilon": "\u03A5", + "/Upsilon1": "\u03D2", + "/Upsilonacute": "\u1FEB", + "/Upsilonacutehooksymbol": "\u03D3", + "/Upsilonacutehooksymbolgreek": "\u03D3", + "/Upsilonadieresishooksymbol": "\u03D4", + "/Upsilonafrican": "\u01B1", + "/Upsilonasper": "\u1F59", + "/Upsilonasperacute": "\u1F5D", + "/Upsilonaspergrave": "\u1F5B", + "/Upsilonaspertilde": "\u1F5F", + "/Upsilonbreve": "\u1FE8", + "/Upsilondieresis": "\u03AB", + "/Upsilondieresishooksymbolgreek": "\u03D4", + "/Upsilongrave": "\u1FEA", + "/Upsilonhooksymbol": "\u03D2", + "/Upsilontonos": "\u038E", + "/Upsilonwithmacron": "\u1FE9", + "/Uring": "\u016E", + "/Ushortcyr": "\u040E", + "/Ushortcyrillic": "\u040E", + "/Usmall": "\uF775", + "/Usquare": "\u1F144", + "/Usquareblack": "\u1F184", + "/Ustraightcyr": "\u04AE", + "/Ustraightcyrillic": "\u04AE", + "/Ustraightstrokecyr": "\u04B0", + "/Ustraightstrokecyrillic": "\u04B0", + "/Utilde": "\u0168", + "/Utildeacute": "\u1E78", + "/Utildebelow": "\u1E74", + "/V": "\u0056", + "/Vcircle": "\u24CB", + "/Vcircleblack": "\u1F165", + "/Vdiagonalstroke": "\uA75E", + "/Vdotbelow": "\u1E7E", + "/Vecyr": "\u0412", + "/Vecyrillic": "\u0412", + "/Vend": "\uA768", + "/Vewarmenian": "\u054E", + "/Vhook": "\u01B2", + "/Visigothicz": "\uA762", + "/Vmod": "\u2C7D", + "/Vmonospace": "\uFF36", + "/Voarmenian": "\u0548", + "/Volapukae": "\uA79A", + "/Volapukoe": "\uA79C", + "/Volapukue": "\uA79E", + "/Vparens": "\u1F125", + "/Vsmall": "\uF776", + "/Vsquare": "\u1F145", + "/Vsquareblack": "\u1F185", + "/Vtilde": "\u1E7C", + "/Vturned": "\u0245", + "/Vwelsh": "\u1EFC", + "/Vy": "\uA760", + "/W": "\u0057", + "/WZcircle": "\u1F12E", + "/Wacute": "\u1E82", + "/Wasallam": "\uFDF8", + "/Wcircle": "\u24CC", + "/Wcircleblack": "\u1F166", + "/Wcircumflex": "\u0174", + "/Wdieresis": "\u1E84", + "/Wdot": "\u1E86", + "/Wdotaccent": "\u1E86", + "/Wdotbelow": "\u1E88", + "/Wecyr": "\u051C", + "/Wgrave": "\u1E80", + "/Whook": "\u2C72", + "/Wmonospace": "\uFF37", + "/Wparens": "\u1F126", + "/Wsmall": "\uF777", + "/Wsquare": "\u1F146", + "/Wsquareblack": "\u1F186", + "/Wynn": "\u01F7", + "/X": "\u0058", + "/Xatailcyr": "\u04B2", + "/Xcircle": "\u24CD", + "/Xcircleblack": "\u1F167", + "/Xdieresis": "\u1E8C", + "/Xdot": "\u1E8A", + "/Xdotaccent": "\u1E8A", + "/Xeharmenian": "\u053D", + "/Xi": "\u039E", + "/Xmonospace": "\uFF38", + "/Xparens": "\u1F127", + "/Xsmall": "\uF778", + "/Xsquare": "\u1F147", + "/Xsquareblack": "\u1F187", + "/Y": "\u0059", + "/Yacute": "\u00DD", + "/Yacutesmall": "\uF7FD", + "/Yacyr": "\u042F", + "/Yaecyr": "\u0518", + "/Yatcyr": "\u0462", + "/Yatcyrillic": "\u0462", + "/Ycircle": "\u24CE", + "/Ycircleblack": "\u1F168", + "/Ycircumflex": "\u0176", + "/Ydieresis": "\u0178", + "/Ydieresissmall": "\uF7FF", + "/Ydot": "\u1E8E", + "/Ydotaccent": "\u1E8E", + "/Ydotbelow": "\u1EF4", + "/Yericyrillic": "\u042B", + "/Yerudieresiscyrillic": "\u04F8", + "/Ygrave": "\u1EF2", + "/Yhoi": "\u1EF6", + "/Yhook": "\u01B3", + "/Yhookabove": "\u1EF6", + "/Yiarmenian": "\u0545", + "/Yicyrillic": "\u0407", + "/Yiwnarmenian": "\u0552", + "/Ylongcyr": "\u042B", + "/Ylongdieresiscyr": "\u04F8", + "/Yloop": "\u1EFE", + "/Ymacron": "\u0232", + "/Ymonospace": "\uFF39", + "/Yogh": "\u021C", + "/Yot": "\u037F", + "/Yparens": "\u1F128", + "/Ysmall": "\uF779", + "/Ysquare": "\u1F148", + "/Ysquareblack": "\u1F188", + "/Ystroke": "\u024E", + "/Ytilde": "\u1EF8", + "/Yturnedsans": "\u2144", + "/Yucyr": "\u042E", + "/Yukrcyr": "\u0407", + "/Yusbigcyr": "\u046A", + "/Yusbigcyrillic": "\u046A", + "/Yusbigiotifiedcyr": "\u046C", + "/Yusbigiotifiedcyrillic": "\u046C", + "/Yuslittlecyr": "\u0466", + "/Yuslittlecyrillic": "\u0466", + "/Yuslittleiotifiedcyr": "\u0468", + "/Yuslittleiotifiedcyrillic": "\u0468", + "/Z": "\u005A", + "/Zaarmenian": "\u0536", + "/Zacute": "\u0179", + "/Zcaron": "\u017D", + "/Zcaronsmall": "\uF6FF", + "/Zcircle": "\u24CF", + "/Zcircleblack": "\u1F169", + "/Zcircumflex": "\u1E90", + "/Zdblstruck": "\u2124", + "/Zdescender": "\u2C6B", + "/Zdot": "\u017B", + "/Zdotaccent": "\u017B", + "/Zdotbelow": "\u1E92", + "/Zecyr": "\u0417", + "/Zecyrillic": "\u0417", + "/Zedescendercyrillic": "\u0498", + "/Zedieresiscyr": "\u04DE", + "/Zedieresiscyrillic": "\u04DE", + "/Zeta": "\u0396", + "/Zetailcyr": "\u0498", + "/Zfraktur": "\u2128", + "/Zhearmenian": "\u053A", + "/Zhebrevecyr": "\u04C1", + "/Zhebrevecyrillic": "\u04C1", + "/Zhecyr": "\u0416", + "/Zhecyrillic": "\u0416", + "/Zhedescendercyrillic": "\u0496", + "/Zhedieresiscyr": "\u04DC", + "/Zhedieresiscyrillic": "\u04DC", + "/Zhetailcyr": "\u0496", + "/Zhook": "\u0224", + "/Zjekomicyr": "\u0504", + "/Zlinebelow": "\u1E94", + "/Zmonospace": "\uFF3A", + "/Zparens": "\u1F129", + "/Zsmall": "\uF77A", + "/Zsquare": "\u1F149", + "/Zsquareblack": "\u1F189", + "/Zstroke": "\u01B5", + "/Zswashtail": "\u2C7F", + "/a": "\u0061", + "/a.inferior": "\u2090", + "/aHonRAA": "\u0613", + "/aa": "\uA733", + "/aabengali": "\u0986", + "/aacute": "\u00E1", + "/aadeva": "\u0906", + "/aagujarati": "\u0A86", + "/aagurmukhi": "\u0A06", + "/aamatragurmukhi": "\u0A3E", + "/aarusquare": "\u3303", + "/aavowelsignbengali": "\u09BE", + "/aavowelsigndeva": "\u093E", + "/aavowelsigngujarati": "\u0ABE", + "/abbreviationmarkarmenian": "\u055F", + "/abbreviationsigndeva": "\u0970", + "/abengali": "\u0985", + "/abopomofo": "\u311A", + "/abreve": "\u0103", + "/abreveacute": "\u1EAF", + "/abrevecyr": "\u04D1", + "/abrevecyrillic": "\u04D1", + "/abrevedotbelow": "\u1EB7", + "/abrevegrave": "\u1EB1", + "/abrevehoi": "\u1EB3", + "/abrevehookabove": "\u1EB3", + "/abrevetilde": "\u1EB5", + "/absquareblack": "\u1F18E", + "/acaron": "\u01CE", + "/accountof": "\u2100", + "/accurrent": "\u23E6", + "/acircle": "\u24D0", + "/acirclekatakana": "\u32D0", + "/acircumflex": "\u00E2", + "/acircumflexacute": "\u1EA5", + "/acircumflexdotbelow": "\u1EAD", + "/acircumflexgrave": "\u1EA7", + "/acircumflexhoi": "\u1EA9", + "/acircumflexhookabove": "\u1EA9", + "/acircumflextilde": "\u1EAB", + "/activatearabicformshaping": "\u206D", + "/activatesymmetricswapping": "\u206B", + "/acute": "\u00B4", + "/acutebelowcmb": "\u0317", + "/acutecmb": "\u0301", + "/acutecomb": "\u0301", + "/acutedblmiddlemod": "\u02F6", + "/acutedeva": "\u0954", + "/acutelowmod": "\u02CF", + "/acutemod": "\u02CA", + "/acutetonecmb": "\u0341", + "/acyr": "\u0430", + "/acyrillic": "\u0430", + "/adblgrave": "\u0201", + "/addakgurmukhi": "\u0A71", + "/addressedsubject": "\u2101", + "/adegadegpada": "\uA9CB", + "/adegpada": "\uA9CA", + "/adeva": "\u0905", + "/adieresis": "\u00E4", + "/adieresiscyr": "\u04D3", + "/adieresiscyrillic": "\u04D3", + "/adieresismacron": "\u01DF", + "/adishakti": "\u262C", + "/admissionTickets": "\u1F39F", + "/adot": "\u0227", + "/adotbelow": "\u1EA1", + "/adotmacron": "\u01E1", + "/ae": "\u00E6", + "/aeacute": "\u01FD", + "/aekorean": "\u3150", + "/aemacron": "\u01E3", + "/aerialTramway": "\u1F6A1", + "/afghani": "\u060B", + "/afii00208": "\u2015", + "/afii08941": "\u20A4", + "/afii10017": "\u0410", + "/afii10018": "\u0411", + "/afii10019": "\u0412", + "/afii10020": "\u0413", + "/afii10021": "\u0414", + "/afii10022": "\u0415", + "/afii10023": "\u0401", + "/afii10024": "\u0416", + "/afii10025": "\u0417", + "/afii10026": "\u0418", + "/afii10027": "\u0419", + "/afii10028": "\u041A", + "/afii10029": "\u041B", + "/afii10030": "\u041C", + "/afii10031": "\u041D", + "/afii10032": "\u041E", + "/afii10033": "\u041F", + "/afii10034": "\u0420", + "/afii10035": "\u0421", + "/afii10036": "\u0422", + "/afii10037": "\u0423", + "/afii10038": "\u0424", + "/afii10039": "\u0425", + "/afii10040": "\u0426", + "/afii10041": "\u0427", + "/afii10042": "\u0428", + "/afii10043": "\u0429", + "/afii10044": "\u042A", + "/afii10045": "\u042B", + "/afii10046": "\u042C", + "/afii10047": "\u042D", + "/afii10048": "\u042E", + "/afii10049": "\u042F", + "/afii10050": "\u0490", + "/afii10051": "\u0402", + "/afii10052": "\u0403", + "/afii10053": "\u0404", + "/afii10054": "\u0405", + "/afii10055": "\u0406", + "/afii10056": "\u0407", + "/afii10057": "\u0408", + "/afii10058": "\u0409", + "/afii10059": "\u040A", + "/afii10060": "\u040B", + "/afii10061": "\u040C", + "/afii10062": "\u040E", + "/afii10063": "\uF6C4", + "/afii10064": "\uF6C5", + "/afii10065": "\u0430", + "/afii10066": "\u0431", + "/afii10067": "\u0432", + "/afii10068": "\u0433", + "/afii10069": "\u0434", + "/afii10070": "\u0435", + "/afii10071": "\u0451", + "/afii10072": "\u0436", + "/afii10073": "\u0437", + "/afii10074": "\u0438", + "/afii10075": "\u0439", + "/afii10076": "\u043A", + "/afii10077": "\u043B", + "/afii10078": "\u043C", + "/afii10079": "\u043D", + "/afii10080": "\u043E", + "/afii10081": "\u043F", + "/afii10082": "\u0440", + "/afii10083": "\u0441", + "/afii10084": "\u0442", + "/afii10085": "\u0443", + "/afii10086": "\u0444", + "/afii10087": "\u0445", + "/afii10088": "\u0446", + "/afii10089": "\u0447", + "/afii10090": "\u0448", + "/afii10091": "\u0449", + "/afii10092": "\u044A", + "/afii10093": "\u044B", + "/afii10094": "\u044C", + "/afii10095": "\u044D", + "/afii10096": "\u044E", + "/afii10097": "\u044F", + "/afii10098": "\u0491", + "/afii10099": "\u0452", + "/afii10100": "\u0453", + "/afii10101": "\u0454", + "/afii10102": "\u0455", + "/afii10103": "\u0456", + "/afii10104": "\u0457", + "/afii10105": "\u0458", + "/afii10106": "\u0459", + "/afii10107": "\u045A", + "/afii10108": "\u045B", + "/afii10109": "\u045C", + "/afii10110": "\u045E", + "/afii10145": "\u040F", + "/afii10146": "\u0462", + "/afii10147": "\u0472", + "/afii10148": "\u0474", + "/afii10192": "\uF6C6", + "/afii10193": "\u045F", + "/afii10194": "\u0463", + "/afii10195": "\u0473", + "/afii10196": "\u0475", + "/afii10831": "\uF6C7", + "/afii10832": "\uF6C8", + "/afii10846": "\u04D9", + "/afii299": "\u200E", + "/afii300": "\u200F", + "/afii301": "\u200D", + "/afii57381": "\u066A", + "/afii57388": "\u060C", + "/afii57392": "\u0660", + "/afii57393": "\u0661", + "/afii57394": "\u0662", + "/afii57395": "\u0663", + "/afii57396": "\u0664", + "/afii57397": "\u0665", + "/afii57398": "\u0666", + "/afii57399": "\u0667", + "/afii57400": "\u0668", + "/afii57401": "\u0669", + "/afii57403": "\u061B", + "/afii57407": "\u061F", + "/afii57409": "\u0621", + "/afii57410": "\u0622", + "/afii57411": "\u0623", + "/afii57412": "\u0624", + "/afii57413": "\u0625", + "/afii57414": "\u0626", + "/afii57415": "\u0627", + "/afii57416": "\u0628", + "/afii57417": "\u0629", + "/afii57418": "\u062A", + "/afii57419": "\u062B", + "/afii57420": "\u062C", + "/afii57421": "\u062D", + "/afii57422": "\u062E", + "/afii57423": "\u062F", + "/afii57424": "\u0630", + "/afii57425": "\u0631", + "/afii57426": "\u0632", + "/afii57427": "\u0633", + "/afii57428": "\u0634", + "/afii57429": "\u0635", + "/afii57430": "\u0636", + "/afii57431": "\u0637", + "/afii57432": "\u0638", + "/afii57433": "\u0639", + "/afii57434": "\u063A", + "/afii57440": "\u0640", + "/afii57441": "\u0641", + "/afii57442": "\u0642", + "/afii57443": "\u0643", + "/afii57444": "\u0644", + "/afii57445": "\u0645", + "/afii57446": "\u0646", + "/afii57448": "\u0648", + "/afii57449": "\u0649", + "/afii57450": "\u064A", + "/afii57451": "\u064B", + "/afii57452": "\u064C", + "/afii57453": "\u064D", + "/afii57454": "\u064E", + "/afii57455": "\u064F", + "/afii57456": "\u0650", + "/afii57457": "\u0651", + "/afii57458": "\u0652", + "/afii57470": "\u0647", + "/afii57505": "\u06A4", + "/afii57506": "\u067E", + "/afii57507": "\u0686", + "/afii57508": "\u0698", + "/afii57509": "\u06AF", + "/afii57511": "\u0679", + "/afii57512": "\u0688", + "/afii57513": "\u0691", + "/afii57514": "\u06BA", + "/afii57519": "\u06D2", + "/afii57534": "\u06D5", + "/afii57636": "\u20AA", + "/afii57645": "\u05BE", + "/afii57658": "\u05C3", + "/afii57664": "\u05D0", + "/afii57665": "\u05D1", + "/afii57666": "\u05D2", + "/afii57667": "\u05D3", + "/afii57668": "\u05D4", + "/afii57669": "\u05D5", + "/afii57670": "\u05D6", + "/afii57671": "\u05D7", + "/afii57672": "\u05D8", + "/afii57673": "\u05D9", + "/afii57674": "\u05DA", + "/afii57675": "\u05DB", + "/afii57676": "\u05DC", + "/afii57677": "\u05DD", + "/afii57678": "\u05DE", + "/afii57679": "\u05DF", + "/afii57680": "\u05E0", + "/afii57681": "\u05E1", + "/afii57682": "\u05E2", + "/afii57683": "\u05E3", + "/afii57684": "\u05E4", + "/afii57685": "\u05E5", + "/afii57686": "\u05E6", + "/afii57687": "\u05E7", + "/afii57688": "\u05E8", + "/afii57689": "\u05E9", + "/afii57690": "\u05EA", + "/afii57694": "\uFB2A", + "/afii57695": "\uFB2B", + "/afii57700": "\uFB4B", + "/afii57705": "\uFB1F", + "/afii57716": "\u05F0", + "/afii57717": "\u05F1", + "/afii57718": "\u05F2", + "/afii57723": "\uFB35", + "/afii57793": "\u05B4", + "/afii57794": "\u05B5", + "/afii57795": "\u05B6", + "/afii57796": "\u05BB", + "/afii57797": "\u05B8", + "/afii57798": "\u05B7", + "/afii57799": "\u05B0", + "/afii57800": "\u05B2", + "/afii57801": "\u05B1", + "/afii57802": "\u05B3", + "/afii57803": "\u05C2", + "/afii57804": "\u05C1", + "/afii57806": "\u05B9", + "/afii57807": "\u05BC", + "/afii57839": "\u05BD", + "/afii57841": "\u05BF", + "/afii57842": "\u05C0", + "/afii57929": "\u02BC", + "/afii61248": "\u2105", + "/afii61289": "\u2113", + "/afii61352": "\u2116", + "/afii61573": "\u202C", + "/afii61574": "\u202D", + "/afii61575": "\u202E", + "/afii61664": "\u200C", + "/afii63167": "\u066D", + "/afii64937": "\u02BD", + "/agrave": "\u00E0", + "/agravedbl": "\u0201", + "/agujarati": "\u0A85", + "/agurmukhi": "\u0A05", + "/ahiragana": "\u3042", + "/ahoi": "\u1EA3", + "/ahookabove": "\u1EA3", + "/aibengali": "\u0990", + "/aibopomofo": "\u311E", + "/aideva": "\u0910", + "/aiecyr": "\u04D5", + "/aiecyrillic": "\u04D5", + "/aigujarati": "\u0A90", + "/aigurmukhi": "\u0A10", + "/aimatragurmukhi": "\u0A48", + "/ain.fina": "\uFECA", + "/ain.init": "\uFECB", + "/ain.init_alefmaksura.fina": "\uFCF7", + "/ain.init_jeem.fina": "\uFC29", + "/ain.init_jeem.medi": "\uFCBA", + "/ain.init_jeem.medi_meem.medi": "\uFDC4", + "/ain.init_meem.fina": "\uFC2A", + "/ain.init_meem.medi": "\uFCBB", + "/ain.init_meem.medi_meem.medi": "\uFD77", + "/ain.init_yeh.fina": "\uFCF8", + "/ain.isol": "\uFEC9", + "/ain.medi": "\uFECC", + "/ain.medi_alefmaksura.fina": "\uFD13", + "/ain.medi_jeem.medi_meem.fina": "\uFD75", + "/ain.medi_meem.medi_alefmaksura.fina": "\uFD78", + "/ain.medi_meem.medi_meem.fina": "\uFD76", + "/ain.medi_meem.medi_yeh.fina": "\uFDB6", + "/ain.medi_yeh.fina": "\uFD14", + "/ainThreeDotsDownAbove": "\u075E", + "/ainTwoDotsAbove": "\u075D", + "/ainTwoDotsVerticallyAbove": "\u075F", + "/ainarabic": "\u0639", + "/ainfinalarabic": "\uFECA", + "/aininitialarabic": "\uFECB", + "/ainmedialarabic": "\uFECC", + "/ainthreedotsabove": "\u06A0", + "/ainvertedbreve": "\u0203", + "/airplaneArriving": "\u1F6EC", + "/airplaneDeparture": "\u1F6EB", + "/aivowelsignbengali": "\u09C8", + "/aivowelsigndeva": "\u0948", + "/aivowelsigngujarati": "\u0AC8", + "/akatakana": "\u30A2", + "/akatakanahalfwidth": "\uFF71", + "/akorean": "\u314F", + "/aktieselskab": "\u214D", + "/alarmclock": "\u23F0", + "/alef": "\u05D0", + "/alef.fina": "\uFE8E", + "/alef.init_fathatan.fina": "\uFD3D", + "/alef.isol": "\uFE8D", + "/alef.medi_fathatan.fina": "\uFD3C", + "/alef:hb": "\u05D0", + "/alefDigitThreeAbove": "\u0774", + "/alefDigitTwoAbove": "\u0773", + "/alefLamYehabove": "\u0616", + "/alefabove": "\u0670", + "/alefarabic": "\u0627", + "/alefdageshhebrew": "\uFB30", + "/aleffinalarabic": "\uFE8E", + "/alefhamza": "\u0623", + "/alefhamza.fina": "\uFE84", + "/alefhamza.isol": "\uFE83", + "/alefhamzaabovearabic": "\u0623", + "/alefhamzaabovefinalarabic": "\uFE84", + "/alefhamzabelow": "\u0625", + "/alefhamzabelow.fina": "\uFE88", + "/alefhamzabelow.isol": "\uFE87", + "/alefhamzabelowarabic": "\u0625", + "/alefhamzabelowfinalarabic": "\uFE88", + "/alefhebrew": "\u05D0", + "/alefhighhamza": "\u0675", + "/aleflamedhebrew": "\uFB4F", + "/alefmadda": "\u0622", + "/alefmadda.fina": "\uFE82", + "/alefmadda.isol": "\uFE81", + "/alefmaddaabovearabic": "\u0622", + "/alefmaddaabovefinalarabic": "\uFE82", + "/alefmaksura": "\u0649", + "/alefmaksura.fina": "\uFEF0", + "/alefmaksura.init_superscriptalef.fina": "\uFC5D", + "/alefmaksura.isol": "\uFEEF", + "/alefmaksura.medi_superscriptalef.fina": "\uFC90", + "/alefmaksuraarabic": "\u0649", + "/alefmaksurafinalarabic": "\uFEF0", + "/alefmaksurainitialarabic": "\uFEF3", + "/alefmaksuramedialarabic": "\uFEF4", + "/alefpatahhebrew": "\uFB2E", + "/alefqamatshebrew": "\uFB2F", + "/alefwasla": "\u0671", + "/alefwasla.fina": "\uFB51", + "/alefwasla.isol": "\uFB50", + "/alefwavyhamza": "\u0672", + "/alefwavyhamzabelow": "\u0673", + "/alefwide:hb": "\uFB21", + "/alefwithmapiq:hb": "\uFB30", + "/alefwithpatah:hb": "\uFB2E", + "/alefwithqamats:hb": "\uFB2F", + "/alembic": "\u2697", + "/aleph": "\u2135", + "/alienMonster": "\u1F47E", + "/allaroundprofile": "\u232E", + "/allequal": "\u224C", + "/allianceideographiccircled": "\u32AF", + "/allianceideographicparen": "\u323F", + "/almostequalorequal": "\u224A", + "/alpha": "\u03B1", + "/alphaacute": "\u1F71", + "/alphaacuteiotasub": "\u1FB4", + "/alphaasper": "\u1F01", + "/alphaasperacute": "\u1F05", + "/alphaasperacuteiotasub": "\u1F85", + "/alphaaspergrave": "\u1F03", + "/alphaaspergraveiotasub": "\u1F83", + "/alphaasperiotasub": "\u1F81", + "/alphaaspertilde": "\u1F07", + "/alphaaspertildeiotasub": "\u1F87", + "/alphabreve": "\u1FB0", + "/alphafunc": "\u237A", + "/alphagrave": "\u1F70", + "/alphagraveiotasub": "\u1FB2", + "/alphaiotasub": "\u1FB3", + "/alphalenis": "\u1F00", + "/alphalenisacute": "\u1F04", + "/alphalenisacuteiotasub": "\u1F84", + "/alphalenisgrave": "\u1F02", + "/alphalenisgraveiotasub": "\u1F82", + "/alphalenisiotasub": "\u1F80", + "/alphalenistilde": "\u1F06", + "/alphalenistildeiotasub": "\u1F86", + "/alphatilde": "\u1FB6", + "/alphatildeiotasub": "\u1FB7", + "/alphatonos": "\u03AC", + "/alphaturned": "\u0252", + "/alphaunderlinefunc": "\u2376", + "/alphawithmacron": "\u1FB1", + "/alternateonewayleftwaytraffic": "\u26D5", + "/alternative": "\u2387", + "/amacron": "\u0101", + "/ambulance": "\u1F691", + "/americanFootball": "\u1F3C8", + "/amfullwidth": "\u33C2", + "/amonospace": "\uFF41", + "/amountofcheck": "\u2447", + "/ampersand": "\u0026", + "/ampersandSindhi": "\u06FD", + "/ampersandmonospace": "\uFF06", + "/ampersandsmall": "\uF726", + "/ampersandturned": "\u214B", + "/amphora": "\u1F3FA", + "/amsquare": "\u33C2", + "/anbopomofo": "\u3122", + "/anchor": "\u2693", + "/ancoradown": "\u2E14", + "/ancoraup": "\u2E15", + "/andappada": "\uA9C3", + "/angbopomofo": "\u3124", + "/anger": "\u1F4A2", + "/angkhankhuthai": "\u0E5A", + "/angle": "\u2220", + "/anglearcright": "\u22BE", + "/anglebracketleft": "\u3008", + "/anglebracketleftvertical": "\uFE3F", + "/anglebracketright": "\u3009", + "/anglebracketrightvertical": "\uFE40", + "/angledottedright": "\u2E16", + "/angleleft": "\u2329", + "/anglemarkerdottedsubstitutionright": "\u2E01", + "/anglemarkersubstitutionright": "\u2E00", + "/angleright": "\u232A", + "/anglezigzagarrowdownright": "\u237C", + "/angryFace": "\u1F620", + "/angstrom": "\u212B", + "/anguishedFace": "\u1F627", + "/ankh": "\u2625", + "/anoteleia": "\u0387", + "/anpeasquare": "\u3302", + "/ant": "\u1F41C", + "/antennaBars": "\u1F4F6", + "/anticlockwiseDownwardsAndUpwardsOpenCircleArrows": "\u1F504", + "/anudattadeva": "\u0952", + "/anusvarabengali": "\u0982", + "/anusvaradeva": "\u0902", + "/anusvaragujarati": "\u0A82", + "/ao": "\uA735", + "/aogonek": "\u0105", + "/aovermfullwidth": "\u33DF", + "/apaatosquare": "\u3300", + "/aparen": "\u249C", + "/aparenthesized": "\u249C", + "/apostrophearmenian": "\u055A", + "/apostrophedblmod": "\u02EE", + "/apostrophemod": "\u02BC", + "/apple": "\uF8FF", + "/approaches": "\u2250", + "/approacheslimit": "\u2250", + "/approxequal": "\u2248", + "/approxequalorimage": "\u2252", + "/approximatelybutnotactuallyequal": "\u2246", + "/approximatelyequal": "\u2245", + "/approximatelyequalorimage": "\u2252", + "/apriltelegraph": "\u32C3", + "/aquarius": "\u2652", + "/ar:ae": "\u06D5", + "/ar:ain": "\u0639", + "/ar:alef": "\u0627", + "/ar:comma": "\u060C", + "/ar:cuberoot": "\u0606", + "/ar:decimalseparator": "\u066B", + "/ar:e": "\u06D0", + "/ar:eight": "\u0668", + "/ar:feh": "\u0641", + "/ar:five": "\u0665", + "/ar:four": "\u0664", + "/ar:fourthroot": "\u0607", + "/ar:kaf": "\u0643", + "/ar:ng": "\u06AD", + "/ar:nine": "\u0669", + "/ar:numbersign": "\u0600", + "/ar:oe": "\u06C6", + "/ar:one": "\u0661", + "/ar:peh": "\u067E", + "/ar:percent": "\u066A", + "/ar:perthousand": "\u060A", + "/ar:question": "\u061F", + "/ar:reh": "\u0631", + "/ar:semicolon": "\u061B", + "/ar:seven": "\u0667", + "/ar:shadda": "\u0651", + "/ar:six": "\u0666", + "/ar:sukun": "\u0652", + "/ar:three": "\u0663", + "/ar:two": "\u0662", + "/ar:u": "\u06C7", + "/ar:ve": "\u06CB", + "/ar:yu": "\u06C8", + "/ar:zero": "\u0660", + "/araeaekorean": "\u318E", + "/araeakorean": "\u318D", + "/arc": "\u2312", + "/archaicmepigraphic": "\uA7FF", + "/aries": "\u2648", + "/arighthalfring": "\u1E9A", + "/aring": "\u00E5", + "/aringacute": "\u01FB", + "/aringbelow": "\u1E01", + "/armn:Ayb": "\u0531", + "/armn:Ben": "\u0532", + "/armn:Ca": "\u053E", + "/armn:Cha": "\u0549", + "/armn:Cheh": "\u0543", + "/armn:Co": "\u0551", + "/armn:DRAMSIGN": "\u058F", + "/armn:Da": "\u0534", + "/armn:Ech": "\u0535", + "/armn:Eh": "\u0537", + "/armn:Et": "\u0538", + "/armn:Feh": "\u0556", + "/armn:Ghad": "\u0542", + "/armn:Gim": "\u0533", + "/armn:Ho": "\u0540", + "/armn:Ini": "\u053B", + "/armn:Ja": "\u0541", + "/armn:Jheh": "\u054B", + "/armn:Keh": "\u0554", + "/armn:Ken": "\u053F", + "/armn:Liwn": "\u053C", + "/armn:Men": "\u0544", + "/armn:Now": "\u0546", + "/armn:Oh": "\u0555", + "/armn:Peh": "\u054A", + "/armn:Piwr": "\u0553", + "/armn:Ra": "\u054C", + "/armn:Reh": "\u0550", + "/armn:Seh": "\u054D", + "/armn:Sha": "\u0547", + "/armn:Tiwn": "\u054F", + "/armn:To": "\u0539", + "/armn:Vew": "\u054E", + "/armn:Vo": "\u0548", + "/armn:Xeh": "\u053D", + "/armn:Yi": "\u0545", + "/armn:Yiwn": "\u0552", + "/armn:Za": "\u0536", + "/armn:Zhe": "\u053A", + "/armn:abbreviationmark": "\u055F", + "/armn:apostrophe": "\u055A", + "/armn:ayb": "\u0561", + "/armn:ben": "\u0562", + "/armn:ca": "\u056E", + "/armn:cha": "\u0579", + "/armn:cheh": "\u0573", + "/armn:co": "\u0581", + "/armn:comma": "\u055D", + "/armn:da": "\u0564", + "/armn:ech": "\u0565", + "/armn:ech_yiwn": "\u0587", + "/armn:eh": "\u0567", + "/armn:emphasismark": "\u055B", + "/armn:et": "\u0568", + "/armn:exclam": "\u055C", + "/armn:feh": "\u0586", + "/armn:ghad": "\u0572", + "/armn:gim": "\u0563", + "/armn:ho": "\u0570", + "/armn:hyphen": "\u058A", + "/armn:ini": "\u056B", + "/armn:ja": "\u0571", + "/armn:jheh": "\u057B", + "/armn:keh": "\u0584", + "/armn:ken": "\u056F", + "/armn:leftfacingeternitysign": "\u058E", + "/armn:liwn": "\u056C", + "/armn:men": "\u0574", + "/armn:men_ech": "\uFB14", + "/armn:men_ini": "\uFB15", + "/armn:men_now": "\uFB13", + "/armn:men_xeh": "\uFB17", + "/armn:now": "\u0576", + "/armn:oh": "\u0585", + "/armn:peh": "\u057A", + "/armn:period": "\u0589", + "/armn:piwr": "\u0583", + "/armn:question": "\u055E", + "/armn:ra": "\u057C", + "/armn:reh": "\u0580", + "/armn:rightfacingeternitysign": "\u058D", + "/armn:ringhalfleft": "\u0559", + "/armn:seh": "\u057D", + "/armn:sha": "\u0577", + "/armn:tiwn": "\u057F", + "/armn:to": "\u0569", + "/armn:vew": "\u057E", + "/armn:vew_now": "\uFB16", + "/armn:vo": "\u0578", + "/armn:xeh": "\u056D", + "/armn:yi": "\u0575", + "/armn:yiwn": "\u0582", + "/armn:za": "\u0566", + "/armn:zhe": "\u056A", + "/arrowNE": "\u2197", + "/arrowNW": "\u2196", + "/arrowSE": "\u2198", + "/arrowSW": "\u2199", + "/arrowanticlockwiseopencircle": "\u21BA", + "/arrowanticlockwisesemicircle": "\u21B6", + "/arrowboth": "\u2194", + "/arrowclockwiseopencircle": "\u21BB", + "/arrowclockwisesemicircle": "\u21B7", + "/arrowdashdown": "\u21E3", + "/arrowdashleft": "\u21E0", + "/arrowdashright": "\u21E2", + "/arrowdashup": "\u21E1", + "/arrowdblboth": "\u21D4", + "/arrowdbldown": "\u21D3", + "/arrowdblleft": "\u21D0", + "/arrowdblright": "\u21D2", + "/arrowdblup": "\u21D1", + "/arrowdown": "\u2193", + "/arrowdowndashed": "\u21E3", + "/arrowdownfrombar": "\u21A7", + "/arrowdownleft": "\u2199", + "/arrowdownright": "\u2198", + "/arrowdowntwoheaded": "\u21A1", + "/arrowdownwhite": "\u21E9", + "/arrowdownzigzag": "\u21AF", + "/arrowheaddown": "\u2304", + "/arrowheaddownlowmod": "\u02EF", + "/arrowheaddownmod": "\u02C5", + "/arrowheadleftlowmod": "\u02F1", + "/arrowheadleftmod": "\u02C2", + "/arrowheadrightlowmod": "\u02F2", + "/arrowheadrightmod": "\u02C3", + "/arrowheadtwobarsuphorizontal": "\u2324", + "/arrowheadup": "\u2303", + "/arrowheaduplowmod": "\u02F0", + "/arrowheadupmod": "\u02C4", + "/arrowhorizex": "\uF8E7", + "/arrowleft": "\u2190", + "/arrowleftdashed": "\u21E0", + "/arrowleftdbl": "\u21D0", + "/arrowleftdblstroke": "\u21CD", + "/arrowleftdowncorner": "\u21B5", + "/arrowleftdowntip": "\u21B2", + "/arrowleftfrombar": "\u21A4", + "/arrowlefthook": "\u21A9", + "/arrowleftloop": "\u21AB", + "/arrowleftlowmod": "\u02FF", + "/arrowleftoverright": "\u21C6", + "/arrowleftoverrighttobar": "\u21B9", + "/arrowleftright": "\u2194", + "/arrowleftrightstroke": "\u21AE", + "/arrowleftrightwave": "\u21AD", + "/arrowleftsquiggle": "\u21DC", + "/arrowleftstroke": "\u219A", + "/arrowlefttail": "\u21A2", + "/arrowlefttobar": "\u21E4", + "/arrowlefttwoheaded": "\u219E", + "/arrowleftuptip": "\u21B0", + "/arrowleftwave": "\u219C", + "/arrowleftwhite": "\u21E6", + "/arrowlongNWtobar": "\u21B8", + "/arrowright": "\u2192", + "/arrowrightdashed": "\u21E2", + "/arrowrightdblstroke": "\u21CF", + "/arrowrightdowncorner": "\u21B4", + "/arrowrightdowntip": "\u21B3", + "/arrowrightfrombar": "\u21A6", + "/arrowrightheavy": "\u279E", + "/arrowrighthook": "\u21AA", + "/arrowrightloop": "\u21AC", + "/arrowrightoverleft": "\u21C4", + "/arrowrightsmallcircle": "\u21F4", + "/arrowrightsquiggle": "\u21DD", + "/arrowrightstroke": "\u219B", + "/arrowrighttail": "\u21A3", + "/arrowrighttobar": "\u21E5", + "/arrowrighttwoheaded": "\u21A0", + "/arrowrightwave": "\u219D", + "/arrowrightwhite": "\u21E8", + "/arrowspaireddown": "\u21CA", + "/arrowspairedleft": "\u21C7", + "/arrowspairedright": "\u21C9", + "/arrowspairedup": "\u21C8", + "/arrowtableft": "\u21E4", + "/arrowtabright": "\u21E5", + "/arrowup": "\u2191", + "/arrowupdashed": "\u21E1", + "/arrowupdn": "\u2195", + "/arrowupdnbse": "\u21A8", + "/arrowupdown": "\u2195", + "/arrowupdownbase": "\u21A8", + "/arrowupdownwithbase": "\u21A8", + "/arrowupfrombar": "\u21A5", + "/arrowupleft": "\u2196", + "/arrowupleftofdown": "\u21C5", + "/arrowupright": "\u2197", + "/arrowuprighttip": "\u21B1", + "/arrowuptwoheaded": "\u219F", + "/arrowupwhite": "\u21E7", + "/arrowvertex": "\uF8E6", + "/articulatedLorry": "\u1F69B", + "/artistPalette": "\u1F3A8", + "/aruhuasquare": "\u3301", + "/asciicircum": "\u005E", + "/asciicircummonospace": "\uFF3E", + "/asciitilde": "\u007E", + "/asciitildemonospace": "\uFF5E", + "/ascript": "\u0251", + "/ascriptturned": "\u0252", + "/asmallhiragana": "\u3041", + "/asmallkatakana": "\u30A1", + "/asmallkatakanahalfwidth": "\uFF67", + "/asper": "\u1FFE", + "/asperacute": "\u1FDE", + "/aspergrave": "\u1FDD", + "/aspertilde": "\u1FDF", + "/assertion": "\u22A6", + "/asterisk": "\u002A", + "/asteriskaltonearabic": "\u066D", + "/asteriskarabic": "\u066D", + "/asteriskmath": "\u2217", + "/asteriskmonospace": "\uFF0A", + "/asterisksmall": "\uFE61", + "/asterism": "\u2042", + "/astonishedFace": "\u1F632", + "/astroke": "\u2C65", + "/astronomicaluranus": "\u26E2", + "/asuperior": "\uF6E9", + "/asympticallyequal": "\u2243", + "/asymptoticallyequal": "\u2243", + "/at": "\u0040", + "/athleticShoe": "\u1F45F", + "/atilde": "\u00E3", + "/atmonospace": "\uFF20", + "/atnachHafukh:hb": "\u05A2", + "/atom": "\u269B", + "/atsmall": "\uFE6B", + "/attentionideographiccircled": "\u329F", + "/aturned": "\u0250", + "/au": "\uA737", + "/aubengali": "\u0994", + "/aubergine": "\u1F346", + "/aubopomofo": "\u3120", + "/audeva": "\u0914", + "/aufullwidth": "\u3373", + "/augujarati": "\u0A94", + "/augurmukhi": "\u0A14", + "/augusttelegraph": "\u32C7", + "/aulengthmarkbengali": "\u09D7", + "/aumatragurmukhi": "\u0A4C", + "/austral": "\u20B3", + "/automatedTellerMachine": "\u1F3E7", + "/automobile": "\u1F697", + "/auvowelsignbengali": "\u09CC", + "/auvowelsigndeva": "\u094C", + "/auvowelsigngujarati": "\u0ACC", + "/av": "\uA739", + "/avagrahadeva": "\u093D", + "/avhorizontalbar": "\uA73B", + "/ay": "\uA73D", + "/aybarmenian": "\u0561", + "/ayin": "\u05E2", + "/ayin:hb": "\u05E2", + "/ayinalt:hb": "\uFB20", + "/ayinaltonehebrew": "\uFB20", + "/ayinhebrew": "\u05E2", + "/azla:hb": "\u059C", + "/b": "\u0062", + "/baarerusquare": "\u332D", + "/babengali": "\u09AC", + "/babyAngel": "\u1F47C", + "/babyBottle": "\u1F37C", + "/babyChick": "\u1F424", + "/backLeftwardsArrowAbove": "\u1F519", + "/backOfEnvelope": "\u1F582", + "/backslash": "\u005C", + "/backslashbarfunc": "\u2340", + "/backslashdbl": "\u244A", + "/backslashmonospace": "\uFF3C", + "/bactrianCamel": "\u1F42B", + "/badeva": "\u092C", + "/badmintonRacquetAndShuttlecock": "\u1F3F8", + "/bagdelimitersshapeleft": "\u27C5", + "/bagdelimitersshaperight": "\u27C6", + "/baggageClaim": "\u1F6C4", + "/bagujarati": "\u0AAC", + "/bagurmukhi": "\u0A2C", + "/bahiragana": "\u3070", + "/bahtthai": "\u0E3F", + "/bakatakana": "\u30D0", + "/balloon": "\u1F388", + "/ballotBoldScriptX": "\u1F5F6", + "/ballotBoxBallot": "\u1F5F3", + "/ballotBoxBoldCheck": "\u1F5F9", + "/ballotBoxBoldScriptX": "\u1F5F7", + "/ballotBoxScriptX": "\u1F5F5", + "/ballotScriptX": "\u1F5F4", + "/bamurda": "\uA9A8", + "/banana": "\u1F34C", + "/bank": "\u1F3E6", + "/banknoteDollarSign": "\u1F4B5", + "/banknoteEuroSign": "\u1F4B6", + "/banknotePoundSign": "\u1F4B7", + "/banknoteYenSign": "\u1F4B4", + "/bar": "\u007C", + "/barChart": "\u1F4CA", + "/barberPole": "\u1F488", + "/barfullwidth": "\u3374", + "/barmonospace": "\uFF5C", + "/barquillverticalleft": "\u2E20", + "/barquillverticalright": "\u2E21", + "/baseball": "\u26BE", + "/basketballAndHoop": "\u1F3C0", + "/bath": "\u1F6C0", + "/bathtub": "\u1F6C1", + "/battery": "\u1F50B", + "/bbopomofo": "\u3105", + "/bcircle": "\u24D1", + "/bdot": "\u1E03", + "/bdotaccent": "\u1E03", + "/bdotbelow": "\u1E05", + "/beachUmbrella": "\u1F3D6", + "/beamedAscendingMusicalNotes": "\u1F39C", + "/beamedDescendingMusicalNotes": "\u1F39D", + "/beamedeighthnotes": "\u266B", + "/beamedsixteenthnotes": "\u266C", + "/beamfunc": "\u2336", + "/bearFace": "\u1F43B", + "/beatingHeart": "\u1F493", + "/because": "\u2235", + "/becyr": "\u0431", + "/becyrillic": "\u0431", + "/bed": "\u1F6CF", + "/beeh": "\u067B", + "/beeh.fina": "\uFB53", + "/beeh.init": "\uFB54", + "/beeh.isol": "\uFB52", + "/beeh.medi": "\uFB55", + "/beerMug": "\u1F37A", + "/beetasquare": "\u333C", + "/beh": "\u0628", + "/beh.fina": "\uFE90", + "/beh.init": "\uFE91", + "/beh.init_alefmaksura.fina": "\uFC09", + "/beh.init_hah.fina": "\uFC06", + "/beh.init_hah.medi": "\uFC9D", + "/beh.init_heh.medi": "\uFCA0", + "/beh.init_jeem.fina": "\uFC05", + "/beh.init_jeem.medi": "\uFC9C", + "/beh.init_khah.fina": "\uFC07", + "/beh.init_khah.medi": "\uFC9E", + "/beh.init_meem.fina": "\uFC08", + "/beh.init_meem.medi": "\uFC9F", + "/beh.init_yeh.fina": "\uFC0A", + "/beh.isol": "\uFE8F", + "/beh.medi": "\uFE92", + "/beh.medi_alefmaksura.fina": "\uFC6E", + "/beh.medi_hah.medi_yeh.fina": "\uFDC2", + "/beh.medi_heh.medi": "\uFCE2", + "/beh.medi_khah.medi_yeh.fina": "\uFD9E", + "/beh.medi_meem.fina": "\uFC6C", + "/beh.medi_meem.medi": "\uFCE1", + "/beh.medi_noon.fina": "\uFC6D", + "/beh.medi_reh.fina": "\uFC6A", + "/beh.medi_yeh.fina": "\uFC6F", + "/beh.medi_zain.fina": "\uFC6B", + "/behDotBelowThreeDotsAbove": "\u0751", + "/behInvertedSmallVBelow": "\u0755", + "/behSmallV": "\u0756", + "/behThreeDotsHorizontallyBelow": "\u0750", + "/behThreeDotsUpBelow": "\u0752", + "/behThreeDotsUpBelowTwoDotsAbove": "\u0753", + "/behTwoDotsBelowDotAbove": "\u0754", + "/beharabic": "\u0628", + "/beheh": "\u0680", + "/beheh.fina": "\uFB5B", + "/beheh.init": "\uFB5C", + "/beheh.isol": "\uFB5A", + "/beheh.medi": "\uFB5D", + "/behfinalarabic": "\uFE90", + "/behinitialarabic": "\uFE91", + "/behiragana": "\u3079", + "/behmedialarabic": "\uFE92", + "/behmeeminitialarabic": "\uFC9F", + "/behmeemisolatedarabic": "\uFC08", + "/behnoonfinalarabic": "\uFC6D", + "/bekatakana": "\u30D9", + "/bellCancellationStroke": "\u1F515", + "/bellhopBell": "\u1F6CE", + "/beltbuckle": "\u2444", + "/benarmenian": "\u0562", + "/beng:a": "\u0985", + "/beng:aa": "\u0986", + "/beng:aasign": "\u09BE", + "/beng:abbreviationsign": "\u09FD", + "/beng:ai": "\u0990", + "/beng:aisign": "\u09C8", + "/beng:anji": "\u0980", + "/beng:anusvara": "\u0982", + "/beng:au": "\u0994", + "/beng:aulengthmark": "\u09D7", + "/beng:ausign": "\u09CC", + "/beng:avagraha": "\u09BD", + "/beng:ba": "\u09AC", + "/beng:bha": "\u09AD", + "/beng:ca": "\u099A", + "/beng:candrabindu": "\u0981", + "/beng:cha": "\u099B", + "/beng:currencyoneless": "\u09F8", + "/beng:da": "\u09A6", + "/beng:dda": "\u09A1", + "/beng:ddha": "\u09A2", + "/beng:dha": "\u09A7", + "/beng:e": "\u098F", + "/beng:eight": "\u09EE", + "/beng:esign": "\u09C7", + "/beng:five": "\u09EB", + "/beng:four": "\u09EA", + "/beng:fourcurrencynumerator": "\u09F7", + "/beng:ga": "\u0997", + "/beng:gandamark": "\u09FB", + "/beng:gha": "\u0998", + "/beng:ha": "\u09B9", + "/beng:i": "\u0987", + "/beng:ii": "\u0988", + "/beng:iisign": "\u09C0", + "/beng:isign": "\u09BF", + "/beng:isshar": "\u09FA", + "/beng:ja": "\u099C", + "/beng:jha": "\u099D", + "/beng:ka": "\u0995", + "/beng:kha": "\u0996", + "/beng:khandata": "\u09CE", + "/beng:la": "\u09B2", + "/beng:llvocal": "\u09E1", + "/beng:llvocalsign": "\u09E3", + "/beng:lvocal": "\u098C", + "/beng:lvocalsign": "\u09E2", + "/beng:ma": "\u09AE", + "/beng:na": "\u09A8", + "/beng:nga": "\u0999", + "/beng:nine": "\u09EF", + "/beng:nna": "\u09A3", + "/beng:nukta": "\u09BC", + "/beng:nya": "\u099E", + "/beng:o": "\u0993", + "/beng:one": "\u09E7", + "/beng:onecurrencynumerator": "\u09F4", + "/beng:osign": "\u09CB", + "/beng:pa": "\u09AA", + "/beng:pha": "\u09AB", + "/beng:ra": "\u09B0", + "/beng:ralowdiagonal": "\u09F1", + "/beng:ramiddiagonal": "\u09F0", + "/beng:rha": "\u09DD", + "/beng:rra": "\u09DC", + "/beng:rrvocal": "\u09E0", + "/beng:rrvocalsign": "\u09C4", + "/beng:rupee": "\u09F3", + "/beng:rupeemark": "\u09F2", + "/beng:rvocal": "\u098B", + "/beng:rvocalsign": "\u09C3", + "/beng:sa": "\u09B8", + "/beng:seven": "\u09ED", + "/beng:sha": "\u09B6", + "/beng:six": "\u09EC", + "/beng:sixteencurrencydenominator": "\u09F9", + "/beng:ssa": "\u09B7", + "/beng:ta": "\u09A4", + "/beng:tha": "\u09A5", + "/beng:three": "\u09E9", + "/beng:threecurrencynumerator": "\u09F6", + "/beng:tta": "\u099F", + "/beng:ttha": "\u09A0", + "/beng:two": "\u09E8", + "/beng:twocurrencynumerator": "\u09F5", + "/beng:u": "\u0989", + "/beng:usign": "\u09C1", + "/beng:uu": "\u098A", + "/beng:uusign": "\u09C2", + "/beng:vedicanusvara": "\u09FC", + "/beng:virama": "\u09CD", + "/beng:visarga": "\u0983", + "/beng:ya": "\u09AF", + "/beng:yya": "\u09DF", + "/beng:zero": "\u09E6", + "/bentoBox": "\u1F371", + "/benzenering": "\u232C", + "/benzeneringcircle": "\u23E3", + "/bet": "\u05D1", + "/bet:hb": "\u05D1", + "/beta": "\u03B2", + "/betasymbol": "\u03D0", + "/betasymbolgreek": "\u03D0", + "/betdagesh": "\uFB31", + "/betdageshhebrew": "\uFB31", + "/bethebrew": "\u05D1", + "/betrafehebrew": "\uFB4C", + "/between": "\u226C", + "/betwithdagesh:hb": "\uFB31", + "/betwithrafe:hb": "\uFB4C", + "/bflourish": "\uA797", + "/bhabengali": "\u09AD", + "/bhadeva": "\u092D", + "/bhagujarati": "\u0AAD", + "/bhagurmukhi": "\u0A2D", + "/bhook": "\u0253", + "/bicycle": "\u1F6B2", + "/bicyclist": "\u1F6B4", + "/bihiragana": "\u3073", + "/bikatakana": "\u30D3", + "/bikini": "\u1F459", + "/bilabialclick": "\u0298", + "/billiards": "\u1F3B1", + "/bindigurmukhi": "\u0A02", + "/biohazard": "\u2623", + "/bird": "\u1F426", + "/birthdayCake": "\u1F382", + "/birusquare": "\u3331", + "/bishopblack": "\u265D", + "/bishopwhite": "\u2657", + "/bitcoin": "\u20BF", + "/blackDownPointingBackhandIndex": "\u1F5A3", + "/blackDroplet": "\u1F322", + "/blackFolder": "\u1F5BF", + "/blackHardShellFloppyDisk": "\u1F5AA", + "/blackHeart": "\u1F5A4", + "/blackLeftPointingBackhandIndex": "\u1F59C", + "/blackPennant": "\u1F3F2", + "/blackPushpin": "\u1F588", + "/blackRightPointingBackhandIndex": "\u1F59D", + "/blackRosette": "\u1F3F6", + "/blackSkullAndCrossbones": "\u1F571", + "/blackSquareButton": "\u1F532", + "/blackTouchtoneTelephone": "\u1F57F", + "/blackUpPointingBackhandIndex": "\u1F5A2", + "/blackcircle": "\u25CF", + "/blackcircleforrecord": "\u23FA", + "/blackdiamond": "\u25C6", + "/blackdownpointingtriangle": "\u25BC", + "/blackforstopsquare": "\u23F9", + "/blackleftpointingpointer": "\u25C4", + "/blackleftpointingtriangle": "\u25C0", + "/blacklenticularbracketleft": "\u3010", + "/blacklenticularbracketleftvertical": "\uFE3B", + "/blacklenticularbracketright": "\u3011", + "/blacklenticularbracketrightvertical": "\uFE3C", + "/blacklowerlefttriangle": "\u25E3", + "/blacklowerrighttriangle": "\u25E2", + "/blackmediumpointingtriangledown": "\u23F7", + "/blackmediumpointingtriangleleft": "\u23F4", + "/blackmediumpointingtriangleright": "\u23F5", + "/blackmediumpointingtriangleup": "\u23F6", + "/blackpointingdoubletrianglebarverticalleft": "\u23EE", + "/blackpointingdoubletrianglebarverticalright": "\u23ED", + "/blackpointingdoubletriangledown": "\u23EC", + "/blackpointingdoubletriangleleft": "\u23EA", + "/blackpointingdoubletriangleright": "\u23E9", + "/blackpointingdoubletriangleup": "\u23EB", + "/blackpointingtriangledoublebarverticalright": "\u23EF", + "/blackrectangle": "\u25AC", + "/blackrightpointingpointer": "\u25BA", + "/blackrightpointingtriangle": "\u25B6", + "/blacksmallsquare": "\u25AA", + "/blacksmilingface": "\u263B", + "/blacksquare": "\u25A0", + "/blackstar": "\u2605", + "/blackupperlefttriangle": "\u25E4", + "/blackupperrighttriangle": "\u25E5", + "/blackuppointingsmalltriangle": "\u25B4", + "/blackuppointingtriangle": "\u25B2", + "/blackwardsbulletleft": "\u204C", + "/blackwardsbulletright": "\u204D", + "/blank": "\u2423", + "/blinebelow": "\u1E07", + "/block": "\u2588", + "/blossom": "\u1F33C", + "/blowfish": "\u1F421", + "/blueBook": "\u1F4D8", + "/blueHeart": "\u1F499", + "/bmonospace": "\uFF42", + "/boar": "\u1F417", + "/board": "\u2328", + "/bobaimaithai": "\u0E1A", + "/bohiragana": "\u307C", + "/bokatakana": "\u30DC", + "/bomb": "\u1F4A3", + "/book": "\u1F56E", + "/bookmark": "\u1F516", + "/bookmarkTabs": "\u1F4D1", + "/books": "\u1F4DA", + "/bopo:a": "\u311A", + "/bopo:ai": "\u311E", + "/bopo:an": "\u3122", + "/bopo:ang": "\u3124", + "/bopo:au": "\u3120", + "/bopo:b": "\u3105", + "/bopo:c": "\u3118", + "/bopo:ch": "\u3114", + "/bopo:d": "\u3109", + "/bopo:e": "\u311C", + "/bopo:eh": "\u311D", + "/bopo:ei": "\u311F", + "/bopo:en": "\u3123", + "/bopo:eng": "\u3125", + "/bopo:er": "\u3126", + "/bopo:f": "\u3108", + "/bopo:g": "\u310D", + "/bopo:gn": "\u312C", + "/bopo:h": "\u310F", + "/bopo:i": "\u3127", + "/bopo:ih": "\u312D", + "/bopo:iu": "\u3129", + "/bopo:j": "\u3110", + "/bopo:k": "\u310E", + "/bopo:l": "\u310C", + "/bopo:m": "\u3107", + "/bopo:n": "\u310B", + "/bopo:ng": "\u312B", + "/bopo:o": "\u311B", + "/bopo:ou": "\u3121", + "/bopo:owithdotabove": "\u312E", + "/bopo:p": "\u3106", + "/bopo:q": "\u3111", + "/bopo:r": "\u3116", + "/bopo:s": "\u3119", + "/bopo:sh": "\u3115", + "/bopo:t": "\u310A", + "/bopo:u": "\u3128", + "/bopo:v": "\u312A", + "/bopo:x": "\u3112", + "/bopo:z": "\u3117", + "/bopo:zh": "\u3113", + "/borutosquare": "\u333E", + "/bottlePoppingCork": "\u1F37E", + "/bouquet": "\u1F490", + "/bouquetOfFlowers": "\u1F395", + "/bowAndArrow": "\u1F3F9", + "/bowlOfHygieia": "\u1F54F", + "/bowling": "\u1F3B3", + "/boxlineverticalleft": "\u23B8", + "/boxlineverticalright": "\u23B9", + "/boy": "\u1F466", + "/boys": "\u1F6C9", + "/bparen": "\u249D", + "/bparenthesized": "\u249D", + "/bqfullwidth": "\u33C3", + "/bqsquare": "\u33C3", + "/braceex": "\uF8F4", + "/braceleft": "\u007B", + "/braceleftbt": "\uF8F3", + "/braceleftmid": "\uF8F2", + "/braceleftmonospace": "\uFF5B", + "/braceleftsmall": "\uFE5B", + "/bracelefttp": "\uF8F1", + "/braceleftvertical": "\uFE37", + "/braceright": "\u007D", + "/bracerightbt": "\uF8FE", + "/bracerightmid": "\uF8FD", + "/bracerightmonospace": "\uFF5D", + "/bracerightsmall": "\uFE5C", + "/bracerighttp": "\uF8FC", + "/bracerightvertical": "\uFE38", + "/bracketangledblleft": "\u27EA", + "/bracketangledblright": "\u27EB", + "/bracketangleleft": "\u27E8", + "/bracketangleright": "\u27E9", + "/bracketbottomcurly": "\u23DF", + "/bracketbottomsquare": "\u23B5", + "/bracketcornerupleftsquare": "\u23A1", + "/bracketcorneruprightsquare": "\u23A4", + "/bracketdottedsubstitutionleft": "\u2E04", + "/bracketdottedsubstitutionright": "\u2E05", + "/bracketextensioncurly": "\u23AA", + "/bracketextensionleftsquare": "\u23A2", + "/bracketextensionrightsquare": "\u23A5", + "/brackethalfbottomleft": "\u2E24", + "/brackethalfbottomright": "\u2E25", + "/brackethalftopleft": "\u2E22", + "/brackethalftopright": "\u2E23", + "/brackethookupleftcurly": "\u23A7", + "/brackethookuprightcurly": "\u23AB", + "/bracketleft": "\u005B", + "/bracketleftbt": "\uF8F0", + "/bracketleftex": "\uF8EF", + "/bracketleftmonospace": "\uFF3B", + "/bracketleftsquarequill": "\u2045", + "/bracketlefttp": "\uF8EE", + "/bracketlowercornerleftsquare": "\u23A3", + "/bracketlowercornerrightsquare": "\u23A6", + "/bracketlowerhookleftcurly": "\u23A9", + "/bracketlowerhookrightcurly": "\u23AD", + "/bracketmiddlepieceleftcurly": "\u23A8", + "/bracketmiddlepiecerightcurly": "\u23AC", + "/bracketoverbrackettopbottomsquare": "\u23B6", + "/bracketparaphraselowleft": "\u2E1C", + "/bracketparaphraselowright": "\u2E1D", + "/bracketraisedleft": "\u2E0C", + "/bracketraisedright": "\u2E0D", + "/bracketright": "\u005D", + "/bracketrightbt": "\uF8FB", + "/bracketrightex": "\uF8FA", + "/bracketrightmonospace": "\uFF3D", + "/bracketrightsquarequill": "\u2046", + "/bracketrighttp": "\uF8F9", + "/bracketsectionupleftlowerrightcurly": "\u23B0", + "/bracketsectionuprightlowerleftcurly": "\u23B1", + "/bracketshellbottom": "\u23E1", + "/bracketshelltop": "\u23E0", + "/bracketshellwhiteleft": "\u27EC", + "/bracketshellwhiteright": "\u27ED", + "/bracketsubstitutionleft": "\u2E02", + "/bracketsubstitutionright": "\u2E03", + "/brackettopcurly": "\u23DE", + "/brackettopsquare": "\u23B4", + "/brackettranspositionleft": "\u2E09", + "/brackettranspositionright": "\u2E0A", + "/bracketwhitesquareleft": "\u27E6", + "/bracketwhitesquareright": "\u27E7", + "/branchbankidentification": "\u2446", + "/bread": "\u1F35E", + "/breve": "\u02D8", + "/brevebelowcmb": "\u032E", + "/brevecmb": "\u0306", + "/breveinvertedbelowcmb": "\u032F", + "/breveinvertedcmb": "\u0311", + "/breveinverteddoublecmb": "\u0361", + "/brevemetrical": "\u23D1", + "/brideVeil": "\u1F470", + "/bridgeAtNight": "\u1F309", + "/bridgebelowcmb": "\u032A", + "/bridgeinvertedbelowcmb": "\u033A", + "/briefcase": "\u1F4BC", + "/brll:blank": "\u2800", + "/brokenHeart": "\u1F494", + "/brokenbar": "\u00A6", + "/brokencirclenorthwestarrow": "\u238B", + "/bstroke": "\u0180", + "/bsuperior": "\uF6EA", + "/btopbar": "\u0183", + "/bug": "\u1F41B", + "/buhiragana": "\u3076", + "/buildingConstruction": "\u1F3D7", + "/bukatakana": "\u30D6", + "/bullet": "\u2022", + "/bulletinverse": "\u25D8", + "/bulletoperator": "\u2219", + "/bullhorn": "\u1F56B", + "/bullhornSoundWaves": "\u1F56C", + "/bullseye": "\u25CE", + "/burrito": "\u1F32F", + "/bus": "\u1F68C", + "/busStop": "\u1F68F", + "/bussyerusquare": "\u3334", + "/bustInSilhouette": "\u1F464", + "/bustsInSilhouette": "\u1F465", + "/c": "\u0063", + "/caarmenian": "\u056E", + "/cabengali": "\u099A", + "/cactus": "\u1F335", + "/cacute": "\u0107", + "/cadauna": "\u2106", + "/cadeva": "\u091A", + "/caduceus": "\u2624", + "/cagujarati": "\u0A9A", + "/cagurmukhi": "\u0A1A", + "/cakraconsonant": "\uA9BF", + "/calendar": "\u1F4C5", + "/calfullwidth": "\u3388", + "/callideographicparen": "\u323A", + "/calsquare": "\u3388", + "/camera": "\u1F4F7", + "/cameraFlash": "\u1F4F8", + "/camping": "\u1F3D5", + "/camurda": "\uA996", + "/cancellationX": "\u1F5D9", + "/cancer": "\u264B", + "/candle": "\u1F56F", + "/candrabindubengali": "\u0981", + "/candrabinducmb": "\u0310", + "/candrabindudeva": "\u0901", + "/candrabindugujarati": "\u0A81", + "/candy": "\u1F36C", + "/canoe": "\u1F6F6", + "/capitulum": "\u2E3F", + "/capricorn": "\u2651", + "/capslock": "\u21EA", + "/cardFileBox": "\u1F5C3", + "/cardIndex": "\u1F4C7", + "/cardIndexDividers": "\u1F5C2", + "/careof": "\u2105", + "/caret": "\u2038", + "/caretinsertionpoint": "\u2041", + "/carettildedownfunc": "\u2371", + "/carettildeupfunc": "\u2372", + "/caron": "\u02C7", + "/caronbelowcmb": "\u032C", + "/caroncmb": "\u030C", + "/carouselHorse": "\u1F3A0", + "/carpStreamer": "\u1F38F", + "/carriagereturn": "\u21B5", + "/carsliding": "\u26D0", + "/castle": "\u26EB", + "/cat": "\u1F408", + "/catFace": "\u1F431", + "/catFaceWithTearsOfJoy": "\u1F639", + "/catFaceWithWrySmile": "\u1F63C", + "/caution": "\u2621", + "/cbar": "\uA793", + "/cbopomofo": "\u3118", + "/ccaron": "\u010D", + "/ccedilla": "\u00E7", + "/ccedillaacute": "\u1E09", + "/ccfullwidth": "\u33C4", + "/ccircle": "\u24D2", + "/ccircumflex": "\u0109", + "/ccurl": "\u0255", + "/cdfullwidth": "\u33C5", + "/cdot": "\u010B", + "/cdotaccent": "\u010B", + "/cdotreversed": "\uA73F", + "/cdsquare": "\u33C5", + "/cecak": "\uA981", + "/cecaktelu": "\uA9B3", + "/cedi": "\u20B5", + "/cedilla": "\u00B8", + "/cedillacmb": "\u0327", + "/ceilingleft": "\u2308", + "/ceilingright": "\u2309", + "/celticCross": "\u1F548", + "/cent": "\u00A2", + "/centigrade": "\u2103", + "/centinferior": "\uF6DF", + "/centmonospace": "\uFFE0", + "/centoldstyle": "\uF7A2", + "/centreddotwhitediamond": "\u27D0", + "/centreideographiccircled": "\u32A5", + "/centreline": "\u2104", + "/centrelineverticalsquarewhite": "\u2385", + "/centsuperior": "\uF6E0", + "/ceres": "\u26B3", + "/chaarmenian": "\u0579", + "/chabengali": "\u099B", + "/chadeva": "\u091B", + "/chagujarati": "\u0A9B", + "/chagurmukhi": "\u0A1B", + "/chains": "\u26D3", + "/chair": "\u2441", + "/chamkocircle": "\u327C", + "/charactertie": "\u2040", + "/chartDownwardsTrend": "\u1F4C9", + "/chartUpwardsTrend": "\u1F4C8", + "/chartUpwardsTrendAndYenSign": "\u1F4B9", + "/chbopomofo": "\u3114", + "/cheabkhasiancyrillic": "\u04BD", + "/cheabkhcyr": "\u04BD", + "/cheabkhtailcyr": "\u04BF", + "/checkbox": "\u2610", + "/checkboxchecked": "\u2611", + "/checkboxx": "\u2612", + "/checkmark": "\u2713", + "/checyr": "\u0447", + "/checyrillic": "\u0447", + "/chedescenderabkhasiancyrillic": "\u04BF", + "/chedescendercyrillic": "\u04B7", + "/chedieresiscyr": "\u04F5", + "/chedieresiscyrillic": "\u04F5", + "/cheeringMegaphone": "\u1F4E3", + "/cheharmenian": "\u0573", + "/chekhakascyr": "\u04CC", + "/chekhakassiancyrillic": "\u04CC", + "/chequeredFlag": "\u1F3C1", + "/cherries": "\u1F352", + "/cherryBlossom": "\u1F338", + "/chestnut": "\u1F330", + "/chetailcyr": "\u04B7", + "/chevertcyr": "\u04B9", + "/cheverticalstrokecyrillic": "\u04B9", + "/chi": "\u03C7", + "/chicken": "\u1F414", + "/chieuchacirclekorean": "\u3277", + "/chieuchaparenkorean": "\u3217", + "/chieuchcirclekorean": "\u3269", + "/chieuchkorean": "\u314A", + "/chieuchparenkorean": "\u3209", + "/childrenCrossing": "\u1F6B8", + "/chipmunk": "\u1F43F", + "/chirho": "\u2627", + "/chiron": "\u26B7", + "/chochangthai": "\u0E0A", + "/chochanthai": "\u0E08", + "/chochingthai": "\u0E09", + "/chochoethai": "\u0E0C", + "/chocolateBar": "\u1F36B", + "/chook": "\u0188", + "/christmasTree": "\u1F384", + "/church": "\u26EA", + "/cieucacirclekorean": "\u3276", + "/cieucaparenkorean": "\u3216", + "/cieuccirclekorean": "\u3268", + "/cieuckorean": "\u3148", + "/cieucparenkorean": "\u3208", + "/cieucuparenkorean": "\u321C", + "/cinema": "\u1F3A6", + "/circle": "\u25CB", + "/circleallbutupperquadrantleftblack": "\u25D5", + "/circlebackslashfunc": "\u2349", + "/circleblack": "\u25CF", + "/circledCrossPommee": "\u1F540", + "/circledInformationSource": "\u1F6C8", + "/circledasteriskoperator": "\u229B", + "/circledbarnotchhorizontal": "\u2389", + "/circledcrossinglanes": "\u26D2", + "/circleddash": "\u229D", + "/circleddivisionslash": "\u2298", + "/circleddotoperator": "\u2299", + "/circledequals": "\u229C", + "/circlediaeresisfunc": "\u2365", + "/circledminus": "\u2296", + "/circledot": "\u2299", + "/circledotrightwhite": "\u2686", + "/circledotted": "\u25CC", + "/circledringoperator": "\u229A", + "/circledtriangledown": "\u238A", + "/circlehalfleftblack": "\u25D0", + "/circlehalfrightblack": "\u25D1", + "/circleinversewhite": "\u25D9", + "/circlejotfunc": "\u233E", + "/circlelowerhalfblack": "\u25D2", + "/circlelowerquadrantleftwhite": "\u25F5", + "/circlelowerquadrantrightwhite": "\u25F6", + "/circlemultiply": "\u2297", + "/circleot": "\u2299", + "/circleplus": "\u2295", + "/circlepostalmark": "\u3036", + "/circlestarfunc": "\u235F", + "/circlestilefunc": "\u233D", + "/circlestroketwodotsaboveheavy": "\u26E3", + "/circletwodotsblackwhite": "\u2689", + "/circletwodotswhite": "\u2687", + "/circleunderlinefunc": "\u235C", + "/circleupperhalfblack": "\u25D3", + "/circleupperquadrantleftwhite": "\u25F4", + "/circleupperquadrantrightblack": "\u25D4", + "/circleupperquadrantrightwhite": "\u25F7", + "/circleverticalfill": "\u25CD", + "/circlewhite": "\u25CB", + "/circlewhitedotrightblack": "\u2688", + "/circlewithlefthalfblack": "\u25D0", + "/circlewithrighthalfblack": "\u25D1", + "/circumflex": "\u02C6", + "/circumflexbelowcmb": "\u032D", + "/circumflexcmb": "\u0302", + "/circumflexlow": "\uA788", + "/circusTent": "\u1F3AA", + "/cityscape": "\u1F3D9", + "/cityscapeAtDusk": "\u1F306", + "/cjk:ideographiccomma": "\u3001", + "/cjk:tortoiseshellbracketleft": "\u3014", + "/cjk:tortoiseshellbracketright": "\u3015", + "/clamshellMobilePhone": "\u1F581", + "/clapperBoard": "\u1F3AC", + "/clappingHandsSign": "\u1F44F", + "/classicalBuilding": "\u1F3DB", + "/clear": "\u2327", + "/clearscreen": "\u239A", + "/clickalveolar": "\u01C2", + "/clickbilabial": "\u0298", + "/clickdental": "\u01C0", + "/clicklateral": "\u01C1", + "/clickretroflex": "\u01C3", + "/clinkingBeerMugs": "\u1F37B", + "/clipboard": "\u1F4CB", + "/clockFaceEight-thirty": "\u1F563", + "/clockFaceEightOclock": "\u1F557", + "/clockFaceEleven-thirty": "\u1F566", + "/clockFaceElevenOclock": "\u1F55A", + "/clockFaceFive-thirty": "\u1F560", + "/clockFaceFiveOclock": "\u1F554", + "/clockFaceFour-thirty": "\u1F55F", + "/clockFaceFourOclock": "\u1F553", + "/clockFaceNine-thirty": "\u1F564", + "/clockFaceNineOclock": "\u1F558", + "/clockFaceOne-thirty": "\u1F55C", + "/clockFaceOneOclock": "\u1F550", + "/clockFaceSeven-thirty": "\u1F562", + "/clockFaceSevenOclock": "\u1F556", + "/clockFaceSix-thirty": "\u1F561", + "/clockFaceSixOclock": "\u1F555", + "/clockFaceTen-thirty": "\u1F565", + "/clockFaceTenOclock": "\u1F559", + "/clockFaceThree-thirty": "\u1F55E", + "/clockFaceThreeOclock": "\u1F552", + "/clockFaceTwelve-thirty": "\u1F567", + "/clockFaceTwelveOclock": "\u1F55B", + "/clockFaceTwo-thirty": "\u1F55D", + "/clockFaceTwoOclock": "\u1F551", + "/clockwiseDownwardsAndUpwardsOpenCircleArrows": "\u1F503", + "/clockwiseRightAndLeftSemicircleArrows": "\u1F5D8", + "/clockwiseRightwardsAndLeftwardsOpenCircleArrows": "\u1F501", + "/clockwiseRightwardsAndLeftwardsOpenCircleArrowsCircledOneOverlay": "\u1F502", + "/closedBook": "\u1F4D5", + "/closedLockKey": "\u1F510", + "/closedMailboxLoweredFlag": "\u1F4EA", + "/closedMailboxRaisedFlag": "\u1F4EB", + "/closedUmbrella": "\u1F302", + "/closedentryleft": "\u26DC", + "/closeup": "\u2050", + "/cloud": "\u2601", + "/cloudLightning": "\u1F329", + "/cloudRain": "\u1F327", + "/cloudSnow": "\u1F328", + "/cloudTornado": "\u1F32A", + "/clsquare": "\u1F191", + "/club": "\u2663", + "/clubblack": "\u2663", + "/clubsuitblack": "\u2663", + "/clubsuitwhite": "\u2667", + "/clubwhite": "\u2667", + "/cm2fullwidth": "\u33A0", + "/cm3fullwidth": "\u33A4", + "/cmb:a": "\u0363", + "/cmb:aaboveflat": "\u1DD3", + "/cmb:aboveogonek": "\u1DCE", + "/cmb:acute": "\u0301", + "/cmb:acutebelow": "\u0317", + "/cmb:acutegraveacute": "\u1DC9", + "/cmb:acutemacron": "\u1DC7", + "/cmb:acutetone": "\u0341", + "/cmb:adieresis": "\u1DF2", + "/cmb:ae": "\u1DD4", + "/cmb:almostequalabove": "\u034C", + "/cmb:almostequaltobelow": "\u1DFD", + "/cmb:alpha": "\u1DE7", + "/cmb:ao": "\u1DD5", + "/cmb:arrowheadleftbelow": "\u0354", + "/cmb:arrowheadrightabove": "\u0350", + "/cmb:arrowheadrightarrowheadupbelow": "\u0356", + "/cmb:arrowheadrightbelow": "\u0355", + "/cmb:arrowleftrightbelow": "\u034D", + "/cmb:arrowrightdoublebelow": "\u0362", + "/cmb:arrowupbelow": "\u034E", + "/cmb:asteriskbelow": "\u0359", + "/cmb:av": "\u1DD6", + "/cmb:b": "\u1DE8", + "/cmb:belowbreve": "\u032E", + "/cmb:beta": "\u1DE9", + "/cmb:breve": "\u0306", + "/cmb:brevemacron": "\u1DCB", + "/cmb:bridgeabove": "\u0346", + "/cmb:bridgebelow": "\u032A", + "/cmb:c": "\u0368", + "/cmb:candrabindu": "\u0310", + "/cmb:caron": "\u030C", + "/cmb:caronbelow": "\u032C", + "/cmb:ccedilla": "\u1DD7", + "/cmb:cedilla": "\u0327", + "/cmb:circumflex": "\u0302", + "/cmb:circumflexbelow": "\u032D", + "/cmb:commaaccentbelow": "\u0326", + "/cmb:commaturnedabove": "\u0312", + "/cmb:d": "\u0369", + "/cmb:dblarchinvertedbelow": "\u032B", + "/cmb:dbloverline": "\u033F", + "/cmb:dblverticallineabove": "\u030E", + "/cmb:dblverticallinebelow": "\u0348", + "/cmb:deletionmark": "\u1DFB", + "/cmb:dialytikatonos": "\u0344", + "/cmb:dieresis": "\u0308", + "/cmb:dieresisbelow": "\u0324", + "/cmb:dotaboveleft": "\u1DF8", + "/cmb:dotaccent": "\u0307", + "/cmb:dotbelowcomb": "\u0323", + "/cmb:dotrightabove": "\u0358", + "/cmb:dottedacute": "\u1DC1", + "/cmb:dottedgrave": "\u1DC0", + "/cmb:doubleabovecircumflex": "\u1DCD", + "/cmb:doublebelowbreve": "\u035C", + "/cmb:doublebreve": "\u035D", + "/cmb:doubleinvertedbelowbreve": "\u1DFC", + "/cmb:doubleringbelow": "\u035A", + "/cmb:downtackbelow": "\u031E", + "/cmb:e": "\u0364", + "/cmb:equalbelow": "\u0347", + "/cmb:esh": "\u1DEF", + "/cmb:eth": "\u1DD9", + "/cmb:f": "\u1DEB", + "/cmb:fermata": "\u0352", + "/cmb:g": "\u1DDA", + "/cmb:graphemejoiner": "\u034F", + "/cmb:grave": "\u0300", + "/cmb:graveacutegrave": "\u1DC8", + "/cmb:gravebelow": "\u0316", + "/cmb:gravedouble": "\u030F", + "/cmb:gravemacron": "\u1DC5", + "/cmb:gravetone": "\u0340", + "/cmb:gsmall": "\u1DDB", + "/cmb:h": "\u036A", + "/cmb:halfleftringabove": "\u0351", + "/cmb:halfleftringbelow": "\u031C", + "/cmb:halfrightringabove": "\u0357", + "/cmb:halfrightringbelow": "\u0339", + "/cmb:homotheticabove": "\u034B", + "/cmb:hookabove": "\u0309", + "/cmb:horn": "\u031B", + "/cmb:hungarumlaut": "\u030B", + "/cmb:i": "\u0365", + "/cmb:insulard": "\u1DD8", + "/cmb:invertedbelowbreve": "\u032F", + "/cmb:invertedbreve": "\u0311", + "/cmb:invertedbridgebelow": "\u033A", + "/cmb:inverteddoublebreve": "\u0361", + "/cmb:iotasub": "\u0345", + "/cmb:isbelow": "\u1DD0", + "/cmb:k": "\u1DDC", + "/cmb:kavykaaboveleft": "\u1DF7", + "/cmb:kavykaaboveright": "\u1DF6", + "/cmb:koronis": "\u0343", + "/cmb:l": "\u1DDD", + "/cmb:leftangleabove": "\u031A", + "/cmb:leftanglebelow": "\u0349", + "/cmb:leftarrowheadabove": "\u1DFE", + "/cmb:lefttackbelow": "\u0318", + "/cmb:lineverticalabove": "\u030D", + "/cmb:lineverticalbelow": "\u0329", + "/cmb:longs": "\u1DE5", + "/cmb:lowline": "\u0332", + "/cmb:lowlinedouble": "\u0333", + "/cmb:lsmall": "\u1DDE", + "/cmb:lwithdoublemiddletilde": "\u1DEC", + "/cmb:m": "\u036B", + "/cmb:macron": "\u0304", + "/cmb:macronacute": "\u1DC4", + "/cmb:macronbelow": "\u0331", + "/cmb:macronbreve": "\u1DCC", + "/cmb:macrondouble": "\u035E", + "/cmb:macrondoublebelow": "\u035F", + "/cmb:macrongrave": "\u1DC6", + "/cmb:minusbelow": "\u0320", + "/cmb:msmall": "\u1DDF", + "/cmb:n": "\u1DE0", + "/cmb:nottildeabove": "\u034A", + "/cmb:nsmall": "\u1DE1", + "/cmb:o": "\u0366", + "/cmb:odieresis": "\u1DF3", + "/cmb:ogonek": "\u0328", + "/cmb:overlaystrokelong": "\u0336", + "/cmb:overlaystrokeshort": "\u0335", + "/cmb:overline": "\u0305", + "/cmb:owithlightcentralizationstroke": "\u1DED", + "/cmb:p": "\u1DEE", + "/cmb:palatalizedhookbelow": "\u0321", + "/cmb:perispomeni": "\u0342", + "/cmb:plusbelow": "\u031F", + "/cmb:r": "\u036C", + "/cmb:rbelow": "\u1DCA", + "/cmb:retroflexhookbelow": "\u0322", + "/cmb:reversedcommaabove": "\u0314", + "/cmb:rightarrowheadanddownarrowheadbelow": "\u1DFF", + "/cmb:righttackbelow": "\u0319", + "/cmb:ringabove": "\u030A", + "/cmb:ringbelow": "\u0325", + "/cmb:rrotunda": "\u1DE3", + "/cmb:rsmall": "\u1DE2", + "/cmb:s": "\u1DE4", + "/cmb:schwa": "\u1DEA", + "/cmb:seagullbelow": "\u033C", + "/cmb:snakebelow": "\u1DC2", + "/cmb:soliduslongoverlay": "\u0338", + "/cmb:solidusshortoverlay": "\u0337", + "/cmb:squarebelow": "\u033B", + "/cmb:suspensionmark": "\u1DC3", + "/cmb:t": "\u036D", + "/cmb:tilde": "\u0303", + "/cmb:tildebelow": "\u0330", + "/cmb:tildedouble": "\u0360", + "/cmb:tildeoverlay": "\u0334", + "/cmb:tildevertical": "\u033E", + "/cmb:turnedabove": "\u0313", + "/cmb:turnedcommaabove": "\u0315", + "/cmb:u": "\u0367", + "/cmb:udieresis": "\u1DF4", + "/cmb:uptackabove": "\u1DF5", + "/cmb:uptackbelow": "\u031D", + "/cmb:urabove": "\u1DD1", + "/cmb:usabove": "\u1DD2", + "/cmb:uwithlightcentralizationstroke": "\u1DF0", + "/cmb:v": "\u036E", + "/cmb:w": "\u1DF1", + "/cmb:wideinvertedbridgebelow": "\u1DF9", + "/cmb:x": "\u036F", + "/cmb:xabove": "\u033D", + "/cmb:xbelow": "\u0353", + "/cmb:z": "\u1DE6", + "/cmb:zigzagabove": "\u035B", + "/cmb:zigzagbelow": "\u1DCF", + "/cmcubedsquare": "\u33A4", + "/cmfullwidth": "\u339D", + "/cmonospace": "\uFF43", + "/cmsquaredsquare": "\u33A0", + "/cntr:acknowledge": "\u2406", + "/cntr:backspace": "\u2408", + "/cntr:bell": "\u2407", + "/cntr:blank": "\u2422", + "/cntr:cancel": "\u2418", + "/cntr:carriagereturn": "\u240D", + "/cntr:datalinkescape": "\u2410", + "/cntr:delete": "\u2421", + "/cntr:deleteformtwo": "\u2425", + "/cntr:devicecontrolfour": "\u2414", + "/cntr:devicecontrolone": "\u2411", + "/cntr:devicecontrolthree": "\u2413", + "/cntr:devicecontroltwo": "\u2412", + "/cntr:endofmedium": "\u2419", + "/cntr:endoftext": "\u2403", + "/cntr:endoftransmission": "\u2404", + "/cntr:endoftransmissionblock": "\u2417", + "/cntr:enquiry": "\u2405", + "/cntr:escape": "\u241B", + "/cntr:fileseparator": "\u241C", + "/cntr:formfeed": "\u240C", + "/cntr:groupseparator": "\u241D", + "/cntr:horizontaltab": "\u2409", + "/cntr:linefeed": "\u240A", + "/cntr:negativeacknowledge": "\u2415", + "/cntr:newline": "\u2424", + "/cntr:null": "\u2400", + "/cntr:openbox": "\u2423", + "/cntr:recordseparator": "\u241E", + "/cntr:shiftin": "\u240F", + "/cntr:shiftout": "\u240E", + "/cntr:space": "\u2420", + "/cntr:startofheading": "\u2401", + "/cntr:startoftext": "\u2402", + "/cntr:substitute": "\u241A", + "/cntr:substituteformtwo": "\u2426", + "/cntr:synchronousidle": "\u2416", + "/cntr:unitseparator": "\u241F", + "/cntr:verticaltab": "\u240B", + "/coarmenian": "\u0581", + "/cocktailGlass": "\u1F378", + "/coffin": "\u26B0", + "/cofullwidth": "\u33C7", + "/collision": "\u1F4A5", + "/colon": "\u003A", + "/colonequals": "\u2254", + "/colonmod": "\uA789", + "/colonmonetary": "\u20A1", + "/colonmonospace": "\uFF1A", + "/colonraisedmod": "\u02F8", + "/colonsign": "\u20A1", + "/colonsmall": "\uFE55", + "/colontriangularhalfmod": "\u02D1", + "/colontriangularmod": "\u02D0", + "/comet": "\u2604", + "/comma": "\u002C", + "/commaabovecmb": "\u0313", + "/commaaboverightcmb": "\u0315", + "/commaaccent": "\uF6C3", + "/commaarabic": "\u060C", + "/commaarmenian": "\u055D", + "/commabarfunc": "\u236A", + "/commainferior": "\uF6E1", + "/commamonospace": "\uFF0C", + "/commaraised": "\u2E34", + "/commareversed": "\u2E41", + "/commareversedabovecmb": "\u0314", + "/commareversedmod": "\u02BD", + "/commasmall": "\uFE50", + "/commasuperior": "\uF6E2", + "/commaturnedabovecmb": "\u0312", + "/commaturnedmod": "\u02BB", + "/commercialat": "\uFE6B", + "/commercialminussign": "\u2052", + "/compass": "\u263C", + "/complement": "\u2201", + "/composition": "\u2384", + "/compression": "\u1F5DC", + "/con": "\uA76F", + "/confettiBall": "\u1F38A", + "/confoundedFace": "\u1F616", + "/confusedFace": "\u1F615", + "/congratulationideographiccircled": "\u3297", + "/congratulationideographicparen": "\u3237", + "/congruent": "\u2245", + "/conicaltaper": "\u2332", + "/conjunction": "\u260C", + "/consquareupblack": "\u26FE", + "/constructionSign": "\u1F6A7", + "/constructionWorker": "\u1F477", + "/containsasmembersmall": "\u220D", + "/containsasnormalsubgroorequalup": "\u22B5", + "/containsasnormalsubgroup": "\u22B3", + "/containslonghorizontalstroke": "\u22FA", + "/containsoverbar": "\u22FD", + "/containsoverbarsmall": "\u22FE", + "/containssmallverticalbarhorizontalstroke": "\u22FC", + "/containsverticalbarhorizontalstroke": "\u22FB", + "/continuousunderline": "\u2381", + "/contourintegral": "\u222E", + "/control": "\u2303", + "/controlACK": "\u0006", + "/controlBEL": "\u0007", + "/controlBS": "\u0008", + "/controlCAN": "\u0018", + "/controlCR": "\u000D", + "/controlDC1": "\u0011", + "/controlDC2": "\u0012", + "/controlDC3": "\u0013", + "/controlDC4": "\u0014", + "/controlDEL": "\u007F", + "/controlDLE": "\u0010", + "/controlEM": "\u0019", + "/controlENQ": "\u0005", + "/controlEOT": "\u0004", + "/controlESC": "\u001B", + "/controlETB": "\u0017", + "/controlETX": "\u0003", + "/controlFF": "\u000C", + "/controlFS": "\u001C", + "/controlGS": "\u001D", + "/controlHT": "\u0009", + "/controlKnobs": "\u1F39B", + "/controlLF": "\u000A", + "/controlNAK": "\u0015", + "/controlRS": "\u001E", + "/controlSI": "\u000F", + "/controlSO": "\u000E", + "/controlSOT": "\u0002", + "/controlSTX": "\u0001", + "/controlSUB": "\u001A", + "/controlSYN": "\u0016", + "/controlUS": "\u001F", + "/controlVT": "\u000B", + "/convavediamondwhite": "\u27E1", + "/convenienceStore": "\u1F3EA", + "/cookedRice": "\u1F35A", + "/cookie": "\u1F36A", + "/cooking": "\u1F373", + "/coolsquare": "\u1F192", + "/coproductarray": "\u2210", + "/copyideographiccircled": "\u32A2", + "/copyright": "\u00A9", + "/copyrightsans": "\uF8E9", + "/copyrightserif": "\uF6D9", + "/cornerbottomleft": "\u231E", + "/cornerbottomright": "\u231F", + "/cornerbracketleft": "\u300C", + "/cornerbracketlefthalfwidth": "\uFF62", + "/cornerbracketleftvertical": "\uFE41", + "/cornerbracketright": "\u300D", + "/cornerbracketrighthalfwidth": "\uFF63", + "/cornerbracketrightvertical": "\uFE42", + "/cornerdotupleft": "\u27D4", + "/cornertopleft": "\u231C", + "/cornertopright": "\u231D", + "/coroniseditorial": "\u2E0E", + "/corporationsquare": "\u337F", + "/correctideographiccircled": "\u32A3", + "/corresponds": "\u2258", + "/cosquare": "\u33C7", + "/couchAndLamp": "\u1F6CB", + "/counterbore": "\u2334", + "/countersink": "\u2335", + "/coupleHeart": "\u1F491", + "/coverkgfullwidth": "\u33C6", + "/coverkgsquare": "\u33C6", + "/cow": "\u1F404", + "/cowFace": "\u1F42E", + "/cpalatalhook": "\uA794", + "/cparen": "\u249E", + "/cparenthesized": "\u249E", + "/creditCard": "\u1F4B3", + "/crescentMoon": "\u1F319", + "/creversed": "\u2184", + "/cricketBatAndBall": "\u1F3CF", + "/crocodile": "\u1F40A", + "/cropbottomleft": "\u230D", + "/cropbottomright": "\u230C", + "/croptopleft": "\u230F", + "/croptopright": "\u230E", + "/crossPommee": "\u1F542", + "/crossPommeeHalf-circleBelow": "\u1F541", + "/crossedFlags": "\u1F38C", + "/crossedswords": "\u2694", + "/crossinglanes": "\u26CC", + "/crossmod": "\u02DF", + "/crossofjerusalem": "\u2629", + "/crossoflorraine": "\u2628", + "/crossonshieldblack": "\u26E8", + "/crown": "\u1F451", + "/crrn:rupee": "\u20A8", + "/cruzeiro": "\u20A2", + "/cryingCatFace": "\u1F63F", + "/cryingFace": "\u1F622", + "/crystalBall": "\u1F52E", + "/cstretched": "\u0297", + "/cstroke": "\u023C", + "/cuatrillo": "\uA72D", + "/cuatrillocomma": "\uA72F", + "/curlyand": "\u22CF", + "/curlylogicaland": "\u22CF", + "/curlylogicalor": "\u22CE", + "/curlyor": "\u22CE", + "/currency": "\u00A4", + "/currencyExchange": "\u1F4B1", + "/curryAndRice": "\u1F35B", + "/custard": "\u1F36E", + "/customeraccountnumber": "\u2449", + "/customs": "\u1F6C3", + "/cyclone": "\u1F300", + "/cylindricity": "\u232D", + "/cyrBreve": "\uF6D1", + "/cyrFlex": "\uF6D2", + "/cyrbreve": "\uF6D4", + "/cyrflex": "\uF6D5", + "/d": "\u0064", + "/daarmenian": "\u0564", + "/daasusquare": "\u3324", + "/dabengali": "\u09A6", + "/dad": "\u0636", + "/dad.fina": "\uFEBE", + "/dad.init": "\uFEBF", + "/dad.init_alefmaksura.fina": "\uFD07", + "/dad.init_hah.fina": "\uFC23", + "/dad.init_hah.medi": "\uFCB5", + "/dad.init_jeem.fina": "\uFC22", + "/dad.init_jeem.medi": "\uFCB4", + "/dad.init_khah.fina": "\uFC24", + "/dad.init_khah.medi": "\uFCB6", + "/dad.init_khah.medi_meem.medi": "\uFD70", + "/dad.init_meem.fina": "\uFC25", + "/dad.init_meem.medi": "\uFCB7", + "/dad.init_reh.fina": "\uFD10", + "/dad.init_yeh.fina": "\uFD08", + "/dad.isol": "\uFEBD", + "/dad.medi": "\uFEC0", + "/dad.medi_alefmaksura.fina": "\uFD23", + "/dad.medi_hah.medi_alefmaksura.fina": "\uFD6E", + "/dad.medi_hah.medi_yeh.fina": "\uFDAB", + "/dad.medi_khah.medi_meem.fina": "\uFD6F", + "/dad.medi_reh.fina": "\uFD2C", + "/dad.medi_yeh.fina": "\uFD24", + "/dadarabic": "\u0636", + "/daddotbelow": "\u06FB", + "/dadeva": "\u0926", + "/dadfinalarabic": "\uFEBE", + "/dadinitialarabic": "\uFEBF", + "/dadmedialarabic": "\uFEC0", + "/dafullwidth": "\u3372", + "/dagesh": "\u05BC", + "/dagesh:hb": "\u05BC", + "/dageshhebrew": "\u05BC", + "/dagger": "\u2020", + "/daggerKnife": "\u1F5E1", + "/daggerdbl": "\u2021", + "/daggerwithguardleft": "\u2E36", + "/daggerwithguardright": "\u2E37", + "/dagujarati": "\u0AA6", + "/dagurmukhi": "\u0A26", + "/dahal": "\u068C", + "/dahal.fina": "\uFB85", + "/dahal.isol": "\uFB84", + "/dahiragana": "\u3060", + "/dakatakana": "\u30C0", + "/dal": "\u062F", + "/dal.fina": "\uFEAA", + "/dal.isol": "\uFEA9", + "/dalInvertedSmallVBelow": "\u075A", + "/dalTwoDotsVerticallyBelowSmallTah": "\u0759", + "/dalarabic": "\u062F", + "/daldotbelow": "\u068A", + "/daldotbelowtahsmall": "\u068B", + "/daldownthreedotsabove": "\u068F", + "/dalet": "\u05D3", + "/dalet:hb": "\u05D3", + "/daletdagesh": "\uFB33", + "/daletdageshhebrew": "\uFB33", + "/dalethatafpatah": "\u05D3", + "/dalethatafpatahhebrew": "\u05D3", + "/dalethatafsegol": "\u05D3", + "/dalethatafsegolhebrew": "\u05D3", + "/dalethebrew": "\u05D3", + "/dalethiriq": "\u05D3", + "/dalethiriqhebrew": "\u05D3", + "/daletholam": "\u05D3", + "/daletholamhebrew": "\u05D3", + "/daletpatah": "\u05D3", + "/daletpatahhebrew": "\u05D3", + "/daletqamats": "\u05D3", + "/daletqamatshebrew": "\u05D3", + "/daletqubuts": "\u05D3", + "/daletqubutshebrew": "\u05D3", + "/daletsegol": "\u05D3", + "/daletsegolhebrew": "\u05D3", + "/daletsheva": "\u05D3", + "/daletshevahebrew": "\u05D3", + "/dalettsere": "\u05D3", + "/dalettserehebrew": "\u05D3", + "/daletwide:hb": "\uFB22", + "/daletwithdagesh:hb": "\uFB33", + "/dalfinalarabic": "\uFEAA", + "/dalfourdotsabove": "\u0690", + "/dalinvertedV": "\u06EE", + "/dalring": "\u0689", + "/damahaprana": "\uA9A3", + "/damma": "\u064F", + "/dammaIsol": "\uFE78", + "/dammaMedi": "\uFE79", + "/dammaarabic": "\u064F", + "/dammalowarabic": "\u064F", + "/dammareversed": "\u065D", + "/dammasmall": "\u0619", + "/dammatan": "\u064C", + "/dammatanIsol": "\uFE72", + "/dammatanaltonearabic": "\u064C", + "/dammatanarabic": "\u064C", + "/dancer": "\u1F483", + "/danda": "\u0964", + "/dango": "\u1F361", + "/darga:hb": "\u05A7", + "/dargahebrew": "\u05A7", + "/dargalefthebrew": "\u05A7", + "/darkShade": "\u2593", + "/darkSunglasses": "\u1F576", + "/dashwithupturnleft": "\u2E43", + "/dasiacmbcyr": "\u0485", + "/dasiapneumatacyrilliccmb": "\u0485", + "/dateseparator": "\u060D", + "/dayeighteentelegraph": "\u33F1", + "/dayeighttelegraph": "\u33E7", + "/dayeleventelegraph": "\u33EA", + "/dayfifteentelegraph": "\u33EE", + "/dayfivetelegraph": "\u33E4", + "/dayfourteentelegraph": "\u33ED", + "/dayfourtelegraph": "\u33E3", + "/daynineteentelegraph": "\u33F2", + "/dayninetelegraph": "\u33E8", + "/dayonetelegraph": "\u33E0", + "/dayseventeentelegraph": "\u33F0", + "/dayseventelegraph": "\u33E6", + "/daysixteentelegraph": "\u33EF", + "/daysixtelegraph": "\u33E5", + "/daytentelegraph": "\u33E9", + "/daythirteentelegraph": "\u33EC", + "/daythirtyonetelegraph": "\u33FE", + "/daythirtytelegraph": "\u33FD", + "/daythreetelegraph": "\u33E2", + "/daytwelvetelegraph": "\u33EB", + "/daytwentyeighttelegraph": "\u33FB", + "/daytwentyfivetelegraph": "\u33F8", + "/daytwentyfourtelegraph": "\u33F7", + "/daytwentyninetelegraph": "\u33FC", + "/daytwentyonetelegraph": "\u33F4", + "/daytwentyseventelegraph": "\u33FA", + "/daytwentysixtelegraph": "\u33F9", + "/daytwentytelegraph": "\u33F3", + "/daytwentythreetelegraph": "\u33F6", + "/daytwentytwotelegraph": "\u33F5", + "/daytwotelegraph": "\u33E1", + "/dbdigraph": "\u0238", + "/dbfullwidth": "\u33C8", + "/dblGrave": "\uF6D3", + "/dblanglebracketleft": "\u300A", + "/dblanglebracketleftvertical": "\uFE3D", + "/dblanglebracketright": "\u300B", + "/dblanglebracketrightvertical": "\uFE3E", + "/dblarchinvertedbelowcmb": "\u032B", + "/dblarrowNE": "\u21D7", + "/dblarrowNW": "\u21D6", + "/dblarrowSE": "\u21D8", + "/dblarrowSW": "\u21D9", + "/dblarrowdown": "\u21D3", + "/dblarrowleft": "\u21D4", + "/dblarrowleftright": "\u21D4", + "/dblarrowleftrightstroke": "\u21CE", + "/dblarrowleftstroke": "\u21CD", + "/dblarrowright": "\u21D2", + "/dblarrowrightstroke": "\u21CF", + "/dblarrowup": "\u21D1", + "/dblarrowupdown": "\u21D5", + "/dbldanda": "\u0965", + "/dbldnhorz": "\u2566", + "/dbldnleft": "\u2557", + "/dbldnright": "\u2554", + "/dblgrave": "\uF6D6", + "/dblgravecmb": "\u030F", + "/dblhorz": "\u2550", + "/dblintegral": "\u222C", + "/dbllowline": "\u2017", + "/dbllowlinecmb": "\u0333", + "/dbloverlinecmb": "\u033F", + "/dblprimemod": "\u02BA", + "/dblstrokearrowdown": "\u21DF", + "/dblstrokearrowup": "\u21DE", + "/dbluphorz": "\u2569", + "/dblupleft": "\u255D", + "/dblupright": "\u255A", + "/dblvert": "\u2551", + "/dblverthorz": "\u256C", + "/dblverticalbar": "\u2016", + "/dblverticallineabovecmb": "\u030E", + "/dblvertleft": "\u2563", + "/dblvertright": "\u2560", + "/dbopomofo": "\u3109", + "/dbsquare": "\u33C8", + "/dcaron": "\u010F", + "/dcedilla": "\u1E11", + "/dchecyr": "\u052D", + "/dcircle": "\u24D3", + "/dcircumflexbelow": "\u1E13", + "/dcroat": "\u0111", + "/dcurl": "\u0221", + "/ddabengali": "\u09A1", + "/ddadeva": "\u0921", + "/ddagujarati": "\u0AA1", + "/ddagurmukhi": "\u0A21", + "/ddahal": "\u068D", + "/ddahal.fina": "\uFB83", + "/ddahal.isol": "\uFB82", + "/ddal": "\u0688", + "/ddal.fina": "\uFB89", + "/ddal.isol": "\uFB88", + "/ddalarabic": "\u0688", + "/ddalfinalarabic": "\uFB89", + "/ddamahaprana": "\uA99E", + "/ddblstruckitalic": "\u2146", + "/dddhadeva": "\u095C", + "/ddhabengali": "\u09A2", + "/ddhadeva": "\u0922", + "/ddhagujarati": "\u0AA2", + "/ddhagurmukhi": "\u0A22", + "/ddot": "\u1E0B", + "/ddotaccent": "\u1E0B", + "/ddotbelow": "\u1E0D", + "/decembertelegraph": "\u32CB", + "/deciduousTree": "\u1F333", + "/decimalexponent": "\u23E8", + "/decimalseparatorarabic": "\u066B", + "/decimalseparatorpersian": "\u066B", + "/decreaseFontSize": "\u1F5DB", + "/decyr": "\u0434", + "/decyrillic": "\u0434", + "/degree": "\u00B0", + "/degreecelsius": "\u2103", + "/degreefahrenheit": "\u2109", + "/dehi:hb": "\u05AD", + "/dehihebrew": "\u05AD", + "/dehiragana": "\u3067", + "/deicoptic": "\u03EF", + "/dekatakana": "\u30C7", + "/dekomicyr": "\u0501", + "/deldiaeresisfunc": "\u2362", + "/deleteleft": "\u232B", + "/deleteright": "\u2326", + "/deliveryTruck": "\u1F69A", + "/delstilefunc": "\u2352", + "/delta": "\u03B4", + "/deltaequal": "\u225C", + "/deltastilefunc": "\u234B", + "/deltaturned": "\u018D", + "/deltaunderlinefunc": "\u2359", + "/deltildefunc": "\u236B", + "/denominatorminusonenumeratorbengali": "\u09F8", + "/dentistrybottomverticalleft": "\u23CC", + "/dentistrybottomverticalright": "\u23BF", + "/dentistrycircledownhorizontal": "\u23C1", + "/dentistrycircleuphorizontal": "\u23C2", + "/dentistrycirclevertical": "\u23C0", + "/dentistrydownhorizontal": "\u23C9", + "/dentistrytopverticalleft": "\u23CB", + "/dentistrytopverticalright": "\u23BE", + "/dentistrytriangledownhorizontal": "\u23C4", + "/dentistrytriangleuphorizontal": "\u23C5", + "/dentistrytrianglevertical": "\u23C3", + "/dentistryuphorizontal": "\u23CA", + "/dentistrywavedownhorizontal": "\u23C7", + "/dentistrywaveuphorizontal": "\u23C8", + "/dentistrywavevertical": "\u23C6", + "/departmentStore": "\u1F3EC", + "/derelictHouseBuilding": "\u1F3DA", + "/desert": "\u1F3DC", + "/desertIsland": "\u1F3DD", + "/desisquare": "\u3325", + "/desktopComputer": "\u1F5A5", + "/desktopWindow": "\u1F5D4", + "/deva:a": "\u0905", + "/deva:aa": "\u0906", + "/deva:aasign": "\u093E", + "/deva:abbreviation": "\u0970", + "/deva:acandra": "\u0972", + "/deva:acute": "\u0954", + "/deva:ai": "\u0910", + "/deva:aisign": "\u0948", + "/deva:anudatta": "\u0952", + "/deva:anusvara": "\u0902", + "/deva:ashort": "\u0904", + "/deva:au": "\u0914", + "/deva:ausign": "\u094C", + "/deva:avagraha": "\u093D", + "/deva:aw": "\u0975", + "/deva:awsign": "\u094F", + "/deva:ba": "\u092C", + "/deva:bba": "\u097F", + "/deva:bha": "\u092D", + "/deva:ca": "\u091A", + "/deva:candrabindu": "\u0901", + "/deva:candrabinduinverted": "\u0900", + "/deva:cha": "\u091B", + "/deva:da": "\u0926", + "/deva:danda": "\u0964", + "/deva:dbldanda": "\u0965", + "/deva:dda": "\u0921", + "/deva:ddda": "\u097E", + "/deva:dddha": "\u095C", + "/deva:ddha": "\u0922", + "/deva:dha": "\u0927", + "/deva:dothigh": "\u0971", + "/deva:e": "\u090F", + "/deva:ecandra": "\u090D", + "/deva:eight": "\u096E", + "/deva:eshort": "\u090E", + "/deva:esign": "\u0947", + "/deva:esigncandra": "\u0945", + "/deva:esignprishthamatra": "\u094E", + "/deva:esignshort": "\u0946", + "/deva:fa": "\u095E", + "/deva:five": "\u096B", + "/deva:four": "\u096A", + "/deva:ga": "\u0917", + "/deva:gga": "\u097B", + "/deva:gha": "\u0918", + "/deva:ghha": "\u095A", + "/deva:glottalstop": "\u097D", + "/deva:grave": "\u0953", + "/deva:ha": "\u0939", + "/deva:i": "\u0907", + "/deva:ii": "\u0908", + "/deva:iisign": "\u0940", + "/deva:isign": "\u093F", + "/deva:ja": "\u091C", + "/deva:jha": "\u091D", + "/deva:jja": "\u097C", + "/deva:ka": "\u0915", + "/deva:kha": "\u0916", + "/deva:khha": "\u0959", + "/deva:la": "\u0932", + "/deva:lla": "\u0933", + "/deva:llla": "\u0934", + "/deva:llvocal": "\u0961", + "/deva:llvocalsign": "\u0963", + "/deva:lvocal": "\u090C", + "/deva:lvocalsign": "\u0962", + "/deva:ma": "\u092E", + "/deva:marwaridda": "\u0978", + "/deva:na": "\u0928", + "/deva:nga": "\u0919", + "/deva:nine": "\u096F", + "/deva:nna": "\u0923", + "/deva:nnna": "\u0929", + "/deva:nukta": "\u093C", + "/deva:nya": "\u091E", + "/deva:o": "\u0913", + "/deva:ocandra": "\u0911", + "/deva:oe": "\u0973", + "/deva:oesign": "\u093A", + "/deva:om": "\u0950", + "/deva:one": "\u0967", + "/deva:ooe": "\u0974", + "/deva:ooesign": "\u093B", + "/deva:oshort": "\u0912", + "/deva:osign": "\u094B", + "/deva:osigncandra": "\u0949", + "/deva:osignshort": "\u094A", + "/deva:pa": "\u092A", + "/deva:pha": "\u092B", + "/deva:qa": "\u0958", + "/deva:ra": "\u0930", + "/deva:rha": "\u095D", + "/deva:rra": "\u0931", + "/deva:rrvocal": "\u0960", + "/deva:rrvocalsign": "\u0944", + "/deva:rvocal": "\u090B", + "/deva:rvocalsign": "\u0943", + "/deva:sa": "\u0938", + "/deva:seven": "\u096D", + "/deva:sha": "\u0936", + "/deva:signelongcandra": "\u0955", + "/deva:six": "\u096C", + "/deva:ssa": "\u0937", + "/deva:ta": "\u0924", + "/deva:tha": "\u0925", + "/deva:three": "\u0969", + "/deva:tta": "\u091F", + "/deva:ttha": "\u0920", + "/deva:two": "\u0968", + "/deva:u": "\u0909", + "/deva:udatta": "\u0951", + "/deva:ue": "\u0976", + "/deva:uesign": "\u0956", + "/deva:usign": "\u0941", + "/deva:uu": "\u090A", + "/deva:uue": "\u0977", + "/deva:uuesign": "\u0957", + "/deva:uusign": "\u0942", + "/deva:va": "\u0935", + "/deva:virama": "\u094D", + "/deva:visarga": "\u0903", + "/deva:ya": "\u092F", + "/deva:yaheavy": "\u097A", + "/deva:yya": "\u095F", + "/deva:za": "\u095B", + "/deva:zero": "\u0966", + "/deva:zha": "\u0979", + "/dezh": "\u02A4", + "/dfemaledbl": "\u26A2", + "/dhabengali": "\u09A7", + "/dhadeva": "\u0927", + "/dhagujarati": "\u0AA7", + "/dhagurmukhi": "\u0A27", + "/dhook": "\u0257", + "/diaeresisgreaterfunc": "\u2369", + "/dialytikatonos": "\u0385", + "/dialytikatonoscmb": "\u0344", + "/diametersign": "\u2300", + "/diamond": "\u2666", + "/diamondShapeADotInside": "\u1F4A0", + "/diamondinsquarewhite": "\u26CB", + "/diamondoperator": "\u22C4", + "/diamondsuitwhite": "\u2662", + "/diamondunderlinefunc": "\u235A", + "/diamondwhitewithdiamondsmallblack": "\u25C8", + "/diefive": "\u2684", + "/diefour": "\u2683", + "/dieone": "\u2680", + "/dieresis": "\u00A8", + "/dieresisacute": "\uF6D7", + "/dieresisbelowcmb": "\u0324", + "/dieresiscmb": "\u0308", + "/dieresisgrave": "\uF6D8", + "/dieresistilde": "\u1FC1", + "/dieresistonos": "\u0385", + "/dieselLocomotive": "\u1F6F2", + "/diesix": "\u2685", + "/diethree": "\u2682", + "/dietwo": "\u2681", + "/differencebetween": "\u224F", + "/digamma": "\u03DD", + "/digammapamphylian": "\u0377", + "/digramgreateryang": "\u268C", + "/digramgreateryin": "\u268F", + "/digramlesseryang": "\u268E", + "/digramlesseryin": "\u268D", + "/dihiragana": "\u3062", + "/dikatakana": "\u30C2", + "/dimensionorigin": "\u2331", + "/dingbatSAns-serifzerocircle": "\u1F10B", + "/dingbatSAns-serifzerocircleblack": "\u1F10C", + "/dinsular": "\uA77A", + "/directHit": "\u1F3AF", + "/directcurrentformtwo": "\u2393", + "/dirgamurevowel": "\uA9BB", + "/disabledcar": "\u26CD", + "/disappointedButRelievedFace": "\u1F625", + "/disappointedFace": "\u1F61E", + "/discontinuousunderline": "\u2382", + "/dittomark": "\u3003", + "/divide": "\u00F7", + "/divides": "\u2223", + "/divisionslash": "\u2215", + "/divisiontimes": "\u22C7", + "/divorce": "\u26AE", + "/dizzy": "\u1F4AB", + "/dizzyFace": "\u1F635", + "/djecyr": "\u0452", + "/djecyrillic": "\u0452", + "/djekomicyr": "\u0503", + "/dkshade": "\u2593", + "/dlfullwidth": "\u3397", + "/dlinebelow": "\u1E0F", + "/dlogicalorsquare": "\u27CF", + "/dlogicalsquare": "\u27CE", + "/dlsquare": "\u3397", + "/dm2fullwidth": "\u3378", + "/dm3fullwidth": "\u3379", + "/dmacron": "\u0111", + "/dmaledbl": "\u26A3", + "/dmfullwidth": "\u3377", + "/dmonospace": "\uFF44", + "/dnblock": "\u2584", + "/dndblhorzsng": "\u2565", + "/dndblleftsng": "\u2556", + "/dndblrightsng": "\u2553", + "/dngb:airplane": "\u2708", + "/dngb:arrowfeatheredblackNE": "\u27B6", + "/dngb:arrowfeatheredblackSE": "\u27B4", + "/dngb:arrowfeatheredblackheavyNE": "\u27B9", + "/dngb:arrowfeatheredblackheavySE": "\u27B7", + "/dngb:arrowheadrightblack": "\u27A4", + "/dngb:arrowheadrightthreeDbottomlight": "\u27A3", + "/dngb:arrowheadrightthreeDtoplight": "\u27A2", + "/dngb:arrowheavyNE": "\u279A", + "/dngb:arrowheavySE": "\u2798", + "/dngb:arrowrightbacktiltedshadowedwhite": "\u27AB", + "/dngb:arrowrightblack": "\u27A1", + "/dngb:arrowrightcircledwhiteheavy": "\u27B2", + "/dngb:arrowrightcurvedownblackheavy": "\u27A5", + "/dngb:arrowrightcurveupblackheavy": "\u27A6", + "/dngb:arrowrightfeatheredblack": "\u27B5", + "/dngb:arrowrightfeatheredblackheavy": "\u27B8", + "/dngb:arrowrightfeatheredwhite": "\u27B3", + "/dngb:arrowrightfronttiltedshadowedwhite": "\u27AC", + "/dngb:arrowrightheavy": "\u2799", + "/dngb:arrowrightleftshadedwhite": "\u27AA", + "/dngb:arrowrightoutlinedopen": "\u27BE", + "/dngb:arrowrightpointed": "\u279B", + "/dngb:arrowrightpointedblackheavy": "\u27A8", + "/dngb:arrowrightrightshadedwhite": "\u27A9", + "/dngb:arrowrightroundheavy": "\u279C", + "/dngb:arrowrightsquatblack": "\u27A7", + "/dngb:arrowrighttriangle": "\u279D", + "/dngb:arrowrighttriangledashed": "\u279F", + "/dngb:arrowrighttriangledashedheavy": "\u27A0", + "/dngb:arrowrighttriangleheavy": "\u279E", + "/dngb:arrowrightwedge": "\u27BC", + "/dngb:arrowrightwedgeheavy": "\u27BD", + "/dngb:arrowrightwideheavy": "\u2794", + "/dngb:arrowshadowrightlowerwhiteheavy": "\u27AD", + "/dngb:arrowshadowrightnotchedlowerwhite": "\u27AF", + "/dngb:arrowshadowrightnotchedupperwhite": "\u27B1", + "/dngb:arrowshadowrightupperwhiteheavy": "\u27AE", + "/dngb:arrowteardropright": "\u27BA", + "/dngb:arrowteardroprightheavy": "\u27BB", + "/dngb:asteriskballoon": "\u2749", + "/dngb:asteriskballoonfour": "\u2723", + "/dngb:asteriskballoonheavyfour": "\u2724", + "/dngb:asteriskcentreopen": "\u2732", + "/dngb:asteriskclubfour": "\u2725", + "/dngb:asteriskheavy": "\u2731", + "/dngb:asteriskpointedsixteen": "\u273A", + "/dngb:asteriskteardrop": "\u273B", + "/dngb:asteriskteardropcentreopen": "\u273C", + "/dngb:asteriskteardropfour": "\u2722", + "/dngb:asteriskteardropheavy": "\u273D", + "/dngb:asteriskteardroppinwheelheavy": "\u2743", + "/dngb:asteriskteardroppropellereight": "\u274A", + "/dngb:asteriskteardroppropellerheavyeight": "\u274B", + "/dngb:ballotx": "\u2717", + "/dngb:ballotxheavy": "\u2718", + "/dngb:bracketleftpointedangleheavyornament": "\u2770", + "/dngb:bracketleftpointedanglemediumornament": "\u276C", + "/dngb:bracketrightpointedangleheavyornament": "\u2771", + "/dngb:bracketrightpointedanglemediumornament": "\u276D", + "/dngb:bracketshellleftlightornament": "\u2772", + "/dngb:bracketshellrightlightornament": "\u2773", + "/dngb:check": "\u2713", + "/dngb:checkheavy": "\u2714", + "/dngb:checkwhiteheavy": "\u2705", + "/dngb:chevronsnowflakeheavy": "\u2746", + "/dngb:circleshadowedwhite": "\u274D", + "/dngb:commaheavydoubleornament": "\u275E", + "/dngb:commaheavydoubleturnedornament": "\u275D", + "/dngb:commaheavyornament": "\u275C", + "/dngb:commaheavyturnedornament": "\u275B", + "/dngb:compasstarpointedblackeight": "\u2737", + "/dngb:compasstarpointedblackheavyeight": "\u2738", + "/dngb:cross": "\u274C", + "/dngb:crosscentreopen": "\u271B", + "/dngb:crosscentreopenheavy": "\u271C", + "/dngb:curlybracketleftmediumornament": "\u2774", + "/dngb:curlybracketrightmediumornament": "\u2775", + "/dngb:curlyloop": "\u27B0", + "/dngb:curlyloopdouble": "\u27BF", + "/dngb:curvedstemparagraphsignornament": "\u2761", + "/dngb:diamondminusxblackwhite": "\u2756", + "/dngb:divisionsignheavy": "\u2797", + "/dngb:eightnegativecircled": "\u277D", + "/dngb:eightsanscircled": "\u2787", + "/dngb:eightsansnegativecircled": "\u2791", + "/dngb:envelope": "\u2709", + "/dngb:exclamationheavy": "\u2757", + "/dngb:exclamationheavyornament": "\u2762", + "/dngb:exclamationwhiteornament": "\u2755", + "/dngb:fivenegativecircled": "\u277A", + "/dngb:fivesanscircled": "\u2784", + "/dngb:fivesansnegativecircled": "\u278E", + "/dngb:floralheart": "\u2766", + "/dngb:floralheartbulletrotated": "\u2767", + "/dngb:floretteblack": "\u273F", + "/dngb:floretteoutlinedpetalledblackeight": "\u2741", + "/dngb:florettepetalledblackwhitesix": "\u273E", + "/dngb:florettewhite": "\u2740", + "/dngb:fournegativecircled": "\u2779", + "/dngb:foursanscircled": "\u2783", + "/dngb:foursansnegativecircled": "\u278D", + "/dngb:greekcrossheavy": "\u271A", + "/dngb:greekcrossoutlined": "\u2719", + "/dngb:heartblackheavy": "\u2764", + "/dngb:heartbulletrotatedblackheavy": "\u2765", + "/dngb:heartexclamationheavyornament": "\u2763", + "/dngb:hvictory": "\u270C", + "/dngb:hwriting": "\u270D", + "/dngb:latincross": "\u271D", + "/dngb:latincrossoutlined": "\u271F", + "/dngb:latincrossshadowedwhite": "\u271E", + "/dngb:lowcommaheavydoubleornament": "\u2760", + "/dngb:lowcommaheavyornament": "\u275F", + "/dngb:maltesecross": "\u2720", + "/dngb:minussignheavy": "\u2796", + "/dngb:multiplicationx": "\u2715", + "/dngb:multiplicationxheavy": "\u2716", + "/dngb:nibblack": "\u2712", + "/dngb:nibwhite": "\u2711", + "/dngb:ninenegativecircled": "\u277E", + "/dngb:ninesanscircled": "\u2788", + "/dngb:ninesansnegativecircled": "\u2792", + "/dngb:onenegativecircled": "\u2776", + "/dngb:onesanscircled": "\u2780", + "/dngb:onesansnegativecircled": "\u278A", + "/dngb:parenthesisleftflattenedmediumornament": "\u276A", + "/dngb:parenthesisleftmediumornament": "\u2768", + "/dngb:parenthesisrightflattenedmediumornament": "\u276B", + "/dngb:parenthesisrightmediumornament": "\u2769", + "/dngb:pencil": "\u270F", + "/dngb:pencillowerright": "\u270E", + "/dngb:pencilupperright": "\u2710", + "/dngb:plussignheavy": "\u2795", + "/dngb:questionblackornament": "\u2753", + "/dngb:questionwhiteornament": "\u2754", + "/dngb:quotationleftpointedangleheavyornament": "\u276E", + "/dngb:quotationrightpointedangleheavyornament": "\u276F", + "/dngb:raisedfist": "\u270A", + "/dngb:raisedh": "\u270B", + "/dngb:safetyscissorsblack": "\u2700", + "/dngb:scissorsblack": "\u2702", + "/dngb:scissorslowerblade": "\u2703", + "/dngb:scissorsupperblade": "\u2701", + "/dngb:scissorswhite": "\u2704", + "/dngb:sevennegativecircled": "\u277C", + "/dngb:sevensanscircled": "\u2786", + "/dngb:sevensansnegativecircled": "\u2790", + "/dngb:sixnegativecircled": "\u277B", + "/dngb:sixsanscircled": "\u2785", + "/dngb:sixsansnegativecircled": "\u278F", + "/dngb:snowflake": "\u2744", + "/dngb:snowflaketight": "\u2745", + "/dngb:sparkle": "\u2747", + "/dngb:sparkleheavy": "\u2748", + "/dngb:sparkles": "\u2728", + "/dngb:spokedasteriskeight": "\u2733", + "/dngb:squaredcrossnegative": "\u274E", + "/dngb:squarelowerrightshadowedwhite": "\u2751", + "/dngb:squareshadowlowerrightwhite": "\u274F", + "/dngb:squareshadowupperrightwhite": "\u2750", + "/dngb:squareupperrightshadowedwhite": "\u2752", + "/dngb:starcentreblackwhite": "\u272C", + "/dngb:starcentreopenblack": "\u272B", + "/dngb:starcentreopenpointedcircledeight": "\u2742", + "/dngb:starcircledwhite": "\u272A", + "/dngb:starofdavid": "\u2721", + "/dngb:staroutlinedblack": "\u272D", + "/dngb:staroutlinedblackheavy": "\u272E", + "/dngb:staroutlinedstresswhite": "\u2729", + "/dngb:starpinwheel": "\u272F", + "/dngb:starpointedblackeight": "\u2734", + "/dngb:starpointedblackfour": "\u2726", + "/dngb:starpointedblacksix": "\u2736", + "/dngb:starpointedblacktwelve": "\u2739", + "/dngb:starpointedpinwheeleight": "\u2735", + "/dngb:starpointedwhitefour": "\u2727", + "/dngb:starshadowedwhite": "\u2730", + "/dngb:tapedrive": "\u2707", + "/dngb:telephonelocationsign": "\u2706", + "/dngb:tennegativecircled": "\u277F", + "/dngb:tensanscircled": "\u2789", + "/dngb:tensansnegativecircled": "\u2793", + "/dngb:threenegativecircled": "\u2778", + "/dngb:threesanscircled": "\u2782", + "/dngb:threesansnegativecircled": "\u278C", + "/dngb:twonegativecircled": "\u2777", + "/dngb:twosanscircled": "\u2781", + "/dngb:twosansnegativecircled": "\u278B", + "/dngb:verticalbarheavy": "\u275A", + "/dngb:verticalbarlight": "\u2758", + "/dngb:verticalbarmedium": "\u2759", + "/dnheavyhorzlight": "\u2530", + "/dnheavyleftlight": "\u2512", + "/dnheavyleftuplight": "\u2527", + "/dnheavyrightlight": "\u250E", + "/dnheavyrightuplight": "\u251F", + "/dnheavyuphorzlight": "\u2541", + "/dnlighthorzheavy": "\u252F", + "/dnlightleftheavy": "\u2511", + "/dnlightleftupheavy": "\u2529", + "/dnlightrightheavy": "\u250D", + "/dnlightrightupheavy": "\u2521", + "/dnlightuphorzheavy": "\u2547", + "/dnsnghorzdbl": "\u2564", + "/dnsngleftdbl": "\u2555", + "/dnsngrightdbl": "\u2552", + "/doNotLitter": "\u1F6AF", + "/dochadathai": "\u0E0E", + "/document": "\u1F5CE", + "/documentPicture": "\u1F5BB", + "/documentText": "\u1F5B9", + "/documentTextAndPicture": "\u1F5BA", + "/dodekthai": "\u0E14", + "/doesnotcontainasnormalsubgroorequalup": "\u22ED", + "/doesnotcontainasnormalsubgroup": "\u22EB", + "/doesnotdivide": "\u2224", + "/doesnotforce": "\u22AE", + "/doesnotprecede": "\u2280", + "/doesnotprecedeorequal": "\u22E0", + "/doesnotprove": "\u22AC", + "/doesnotsucceed": "\u2281", + "/doesnotsucceedorequal": "\u22E1", + "/dog": "\u1F415", + "/dogFace": "\u1F436", + "/dohiragana": "\u3069", + "/dokatakana": "\u30C9", + "/dollar": "\u0024", + "/dollarinferior": "\uF6E3", + "/dollarmonospace": "\uFF04", + "/dollaroldstyle": "\uF724", + "/dollarsmall": "\uFE69", + "/dollarsuperior": "\uF6E4", + "/dolphin": "\u1F42C", + "/dominohorizontal_00_00": "\u1F031", + "/dominohorizontal_00_01": "\u1F032", + "/dominohorizontal_00_02": "\u1F033", + "/dominohorizontal_00_03": "\u1F034", + "/dominohorizontal_00_04": "\u1F035", + "/dominohorizontal_00_05": "\u1F036", + "/dominohorizontal_00_06": "\u1F037", + "/dominohorizontal_01_00": "\u1F038", + "/dominohorizontal_01_01": "\u1F039", + "/dominohorizontal_01_02": "\u1F03A", + "/dominohorizontal_01_03": "\u1F03B", + "/dominohorizontal_01_04": "\u1F03C", + "/dominohorizontal_01_05": "\u1F03D", + "/dominohorizontal_01_06": "\u1F03E", + "/dominohorizontal_02_00": "\u1F03F", + "/dominohorizontal_02_01": "\u1F040", + "/dominohorizontal_02_02": "\u1F041", + "/dominohorizontal_02_03": "\u1F042", + "/dominohorizontal_02_04": "\u1F043", + "/dominohorizontal_02_05": "\u1F044", + "/dominohorizontal_02_06": "\u1F045", + "/dominohorizontal_03_00": "\u1F046", + "/dominohorizontal_03_01": "\u1F047", + "/dominohorizontal_03_02": "\u1F048", + "/dominohorizontal_03_03": "\u1F049", + "/dominohorizontal_03_04": "\u1F04A", + "/dominohorizontal_03_05": "\u1F04B", + "/dominohorizontal_03_06": "\u1F04C", + "/dominohorizontal_04_00": "\u1F04D", + "/dominohorizontal_04_01": "\u1F04E", + "/dominohorizontal_04_02": "\u1F04F", + "/dominohorizontal_04_03": "\u1F050", + "/dominohorizontal_04_04": "\u1F051", + "/dominohorizontal_04_05": "\u1F052", + "/dominohorizontal_04_06": "\u1F053", + "/dominohorizontal_05_00": "\u1F054", + "/dominohorizontal_05_01": "\u1F055", + "/dominohorizontal_05_02": "\u1F056", + "/dominohorizontal_05_03": "\u1F057", + "/dominohorizontal_05_04": "\u1F058", + "/dominohorizontal_05_05": "\u1F059", + "/dominohorizontal_05_06": "\u1F05A", + "/dominohorizontal_06_00": "\u1F05B", + "/dominohorizontal_06_01": "\u1F05C", + "/dominohorizontal_06_02": "\u1F05D", + "/dominohorizontal_06_03": "\u1F05E", + "/dominohorizontal_06_04": "\u1F05F", + "/dominohorizontal_06_05": "\u1F060", + "/dominohorizontal_06_06": "\u1F061", + "/dominohorizontalback": "\u1F030", + "/dominovertical_00_00": "\u1F063", + "/dominovertical_00_01": "\u1F064", + "/dominovertical_00_02": "\u1F065", + "/dominovertical_00_03": "\u1F066", + "/dominovertical_00_04": "\u1F067", + "/dominovertical_00_05": "\u1F068", + "/dominovertical_00_06": "\u1F069", + "/dominovertical_01_00": "\u1F06A", + "/dominovertical_01_01": "\u1F06B", + "/dominovertical_01_02": "\u1F06C", + "/dominovertical_01_03": "\u1F06D", + "/dominovertical_01_04": "\u1F06E", + "/dominovertical_01_05": "\u1F06F", + "/dominovertical_01_06": "\u1F070", + "/dominovertical_02_00": "\u1F071", + "/dominovertical_02_01": "\u1F072", + "/dominovertical_02_02": "\u1F073", + "/dominovertical_02_03": "\u1F074", + "/dominovertical_02_04": "\u1F075", + "/dominovertical_02_05": "\u1F076", + "/dominovertical_02_06": "\u1F077", + "/dominovertical_03_00": "\u1F078", + "/dominovertical_03_01": "\u1F079", + "/dominovertical_03_02": "\u1F07A", + "/dominovertical_03_03": "\u1F07B", + "/dominovertical_03_04": "\u1F07C", + "/dominovertical_03_05": "\u1F07D", + "/dominovertical_03_06": "\u1F07E", + "/dominovertical_04_00": "\u1F07F", + "/dominovertical_04_01": "\u1F080", + "/dominovertical_04_02": "\u1F081", + "/dominovertical_04_03": "\u1F082", + "/dominovertical_04_04": "\u1F083", + "/dominovertical_04_05": "\u1F084", + "/dominovertical_04_06": "\u1F085", + "/dominovertical_05_00": "\u1F086", + "/dominovertical_05_01": "\u1F087", + "/dominovertical_05_02": "\u1F088", + "/dominovertical_05_03": "\u1F089", + "/dominovertical_05_04": "\u1F08A", + "/dominovertical_05_05": "\u1F08B", + "/dominovertical_05_06": "\u1F08C", + "/dominovertical_06_00": "\u1F08D", + "/dominovertical_06_01": "\u1F08E", + "/dominovertical_06_02": "\u1F08F", + "/dominovertical_06_03": "\u1F090", + "/dominovertical_06_04": "\u1F091", + "/dominovertical_06_05": "\u1F092", + "/dominovertical_06_06": "\u1F093", + "/dominoverticalback": "\u1F062", + "/dong": "\u20AB", + "/door": "\u1F6AA", + "/dorusquare": "\u3326", + "/dot": "\u27D1", + "/dotaccent": "\u02D9", + "/dotaccentcmb": "\u0307", + "/dotbelowcmb": "\u0323", + "/dotbelowcomb": "\u0323", + "/dotkatakana": "\u30FB", + "/dotlessbeh": "\u066E", + "/dotlessfeh": "\u06A1", + "/dotlessi": "\u0131", + "/dotlessj": "\uF6BE", + "/dotlessjstroke": "\u025F", + "/dotlessjstrokehook": "\u0284", + "/dotlesskhahabove": "\u06E1", + "/dotlessqaf": "\u066F", + "/dotlower:hb": "\u05C5", + "/dotmath": "\u22C5", + "/dotminus": "\u2238", + "/dotplus": "\u2214", + "/dotraised": "\u2E33", + "/dots1": "\u2801", + "/dots12": "\u2803", + "/dots123": "\u2807", + "/dots1234": "\u280F", + "/dots12345": "\u281F", + "/dots123456": "\u283F", + "/dots1234567": "\u287F", + "/dots12345678": "\u28FF", + "/dots1234568": "\u28BF", + "/dots123457": "\u285F", + "/dots1234578": "\u28DF", + "/dots123458": "\u289F", + "/dots12346": "\u282F", + "/dots123467": "\u286F", + "/dots1234678": "\u28EF", + "/dots123468": "\u28AF", + "/dots12347": "\u284F", + "/dots123478": "\u28CF", + "/dots12348": "\u288F", + "/dots1235": "\u2817", + "/dots12356": "\u2837", + "/dots123567": "\u2877", + "/dots1235678": "\u28F7", + "/dots123568": "\u28B7", + "/dots12357": "\u2857", + "/dots123578": "\u28D7", + "/dots12358": "\u2897", + "/dots1236": "\u2827", + "/dots12367": "\u2867", + "/dots123678": "\u28E7", + "/dots12368": "\u28A7", + "/dots1237": "\u2847", + "/dots12378": "\u28C7", + "/dots1238": "\u2887", + "/dots124": "\u280B", + "/dots1245": "\u281B", + "/dots12456": "\u283B", + "/dots124567": "\u287B", + "/dots1245678": "\u28FB", + "/dots124568": "\u28BB", + "/dots12457": "\u285B", + "/dots124578": "\u28DB", + "/dots12458": "\u289B", + "/dots1246": "\u282B", + "/dots12467": "\u286B", + "/dots124678": "\u28EB", + "/dots12468": "\u28AB", + "/dots1247": "\u284B", + "/dots12478": "\u28CB", + "/dots1248": "\u288B", + "/dots125": "\u2813", + "/dots1256": "\u2833", + "/dots12567": "\u2873", + "/dots125678": "\u28F3", + "/dots12568": "\u28B3", + "/dots1257": "\u2853", + "/dots12578": "\u28D3", + "/dots1258": "\u2893", + "/dots126": "\u2823", + "/dots1267": "\u2863", + "/dots12678": "\u28E3", + "/dots1268": "\u28A3", + "/dots127": "\u2843", + "/dots1278": "\u28C3", + "/dots128": "\u2883", + "/dots13": "\u2805", + "/dots134": "\u280D", + "/dots1345": "\u281D", + "/dots13456": "\u283D", + "/dots134567": "\u287D", + "/dots1345678": "\u28FD", + "/dots134568": "\u28BD", + "/dots13457": "\u285D", + "/dots134578": "\u28DD", + "/dots13458": "\u289D", + "/dots1346": "\u282D", + "/dots13467": "\u286D", + "/dots134678": "\u28ED", + "/dots13468": "\u28AD", + "/dots1347": "\u284D", + "/dots13478": "\u28CD", + "/dots1348": "\u288D", + "/dots135": "\u2815", + "/dots1356": "\u2835", + "/dots13567": "\u2875", + "/dots135678": "\u28F5", + "/dots13568": "\u28B5", + "/dots1357": "\u2855", + "/dots13578": "\u28D5", + "/dots1358": "\u2895", + "/dots136": "\u2825", + "/dots1367": "\u2865", + "/dots13678": "\u28E5", + "/dots1368": "\u28A5", + "/dots137": "\u2845", + "/dots1378": "\u28C5", + "/dots138": "\u2885", + "/dots14": "\u2809", + "/dots145": "\u2819", + "/dots1456": "\u2839", + "/dots14567": "\u2879", + "/dots145678": "\u28F9", + "/dots14568": "\u28B9", + "/dots1457": "\u2859", + "/dots14578": "\u28D9", + "/dots1458": "\u2899", + "/dots146": "\u2829", + "/dots1467": "\u2869", + "/dots14678": "\u28E9", + "/dots1468": "\u28A9", + "/dots147": "\u2849", + "/dots1478": "\u28C9", + "/dots148": "\u2889", + "/dots15": "\u2811", + "/dots156": "\u2831", + "/dots1567": "\u2871", + "/dots15678": "\u28F1", + "/dots1568": "\u28B1", + "/dots157": "\u2851", + "/dots1578": "\u28D1", + "/dots158": "\u2891", + "/dots16": "\u2821", + "/dots167": "\u2861", + "/dots1678": "\u28E1", + "/dots168": "\u28A1", + "/dots17": "\u2841", + "/dots178": "\u28C1", + "/dots18": "\u2881", + "/dots2": "\u2802", + "/dots23": "\u2806", + "/dots234": "\u280E", + "/dots2345": "\u281E", + "/dots23456": "\u283E", + "/dots234567": "\u287E", + "/dots2345678": "\u28FE", + "/dots234568": "\u28BE", + "/dots23457": "\u285E", + "/dots234578": "\u28DE", + "/dots23458": "\u289E", + "/dots2346": "\u282E", + "/dots23467": "\u286E", + "/dots234678": "\u28EE", + "/dots23468": "\u28AE", + "/dots2347": "\u284E", + "/dots23478": "\u28CE", + "/dots2348": "\u288E", + "/dots235": "\u2816", + "/dots2356": "\u2836", + "/dots23567": "\u2876", + "/dots235678": "\u28F6", + "/dots23568": "\u28B6", + "/dots2357": "\u2856", + "/dots23578": "\u28D6", + "/dots2358": "\u2896", + "/dots236": "\u2826", + "/dots2367": "\u2866", + "/dots23678": "\u28E6", + "/dots2368": "\u28A6", + "/dots237": "\u2846", + "/dots2378": "\u28C6", + "/dots238": "\u2886", + "/dots24": "\u280A", + "/dots245": "\u281A", + "/dots2456": "\u283A", + "/dots24567": "\u287A", + "/dots245678": "\u28FA", + "/dots24568": "\u28BA", + "/dots2457": "\u285A", + "/dots24578": "\u28DA", + "/dots2458": "\u289A", + "/dots246": "\u282A", + "/dots2467": "\u286A", + "/dots24678": "\u28EA", + "/dots2468": "\u28AA", + "/dots247": "\u284A", + "/dots2478": "\u28CA", + "/dots248": "\u288A", + "/dots25": "\u2812", + "/dots256": "\u2832", + "/dots2567": "\u2872", + "/dots25678": "\u28F2", + "/dots2568": "\u28B2", + "/dots257": "\u2852", + "/dots2578": "\u28D2", + "/dots258": "\u2892", + "/dots26": "\u2822", + "/dots267": "\u2862", + "/dots2678": "\u28E2", + "/dots268": "\u28A2", + "/dots27": "\u2842", + "/dots278": "\u28C2", + "/dots28": "\u2882", + "/dots3": "\u2804", + "/dots34": "\u280C", + "/dots345": "\u281C", + "/dots3456": "\u283C", + "/dots34567": "\u287C", + "/dots345678": "\u28FC", + "/dots34568": "\u28BC", + "/dots3457": "\u285C", + "/dots34578": "\u28DC", + "/dots3458": "\u289C", + "/dots346": "\u282C", + "/dots3467": "\u286C", + "/dots34678": "\u28EC", + "/dots3468": "\u28AC", + "/dots347": "\u284C", + "/dots3478": "\u28CC", + "/dots348": "\u288C", + "/dots35": "\u2814", + "/dots356": "\u2834", + "/dots3567": "\u2874", + "/dots35678": "\u28F4", + "/dots3568": "\u28B4", + "/dots357": "\u2854", + "/dots3578": "\u28D4", + "/dots358": "\u2894", + "/dots36": "\u2824", + "/dots367": "\u2864", + "/dots3678": "\u28E4", + "/dots368": "\u28A4", + "/dots37": "\u2844", + "/dots378": "\u28C4", + "/dots38": "\u2884", + "/dots4": "\u2808", + "/dots45": "\u2818", + "/dots456": "\u2838", + "/dots4567": "\u2878", + "/dots45678": "\u28F8", + "/dots4568": "\u28B8", + "/dots457": "\u2858", + "/dots4578": "\u28D8", + "/dots458": "\u2898", + "/dots46": "\u2828", + "/dots467": "\u2868", + "/dots4678": "\u28E8", + "/dots468": "\u28A8", + "/dots47": "\u2848", + "/dots478": "\u28C8", + "/dots48": "\u2888", + "/dots5": "\u2810", + "/dots56": "\u2830", + "/dots567": "\u2870", + "/dots5678": "\u28F0", + "/dots568": "\u28B0", + "/dots57": "\u2850", + "/dots578": "\u28D0", + "/dots58": "\u2890", + "/dots6": "\u2820", + "/dots67": "\u2860", + "/dots678": "\u28E0", + "/dots68": "\u28A0", + "/dots7": "\u2840", + "/dots78": "\u28C0", + "/dots8": "\u2880", + "/dotsquarefour": "\u2E2C", + "/dottedcircle": "\u25CC", + "/dottedcross": "\u205C", + "/dotupper:hb": "\u05C4", + "/doublebarvertical": "\u23F8", + "/doubleyodpatah": "\uFB1F", + "/doubleyodpatahhebrew": "\uFB1F", + "/doughnut": "\u1F369", + "/doveOfPeace": "\u1F54A", + "/downtackbelowcmb": "\u031E", + "/downtackmod": "\u02D5", + "/downwarrowleftofuparrow": "\u21F5", + "/dparen": "\u249F", + "/dparenthesized": "\u249F", + "/drachma": "\u20AF", + "/dragon": "\u1F409", + "/dragonFace": "\u1F432", + "/draughtskingblack": "\u26C3", + "/draughtskingwhite": "\u26C1", + "/draughtsmanblack": "\u26C2", + "/draughtsmanwhite": "\u26C0", + "/dress": "\u1F457", + "/driveslow": "\u26DA", + "/dromedaryCamel": "\u1F42A", + "/droplet": "\u1F4A7", + "/dsquare": "\u1F1A5", + "/dsuperior": "\uF6EB", + "/dtail": "\u0256", + "/dtopbar": "\u018C", + "/duhiragana": "\u3065", + "/dukatakana": "\u30C5", + "/dul": "\u068E", + "/dul.fina": "\uFB87", + "/dul.isol": "\uFB86", + "/dum": "\uA771", + "/dvd": "\u1F4C0", + "/dyeh": "\u0684", + "/dyeh.fina": "\uFB73", + "/dyeh.init": "\uFB74", + "/dyeh.isol": "\uFB72", + "/dyeh.medi": "\uFB75", + "/dz": "\u01F3", + "/dzaltone": "\u02A3", + "/dzcaron": "\u01C6", + "/dzcurl": "\u02A5", + "/dzeabkhasiancyrillic": "\u04E1", + "/dzeabkhcyr": "\u04E1", + "/dzecyr": "\u0455", + "/dzecyrillic": "\u0455", + "/dzed": "\u02A3", + "/dzedcurl": "\u02A5", + "/dzhecyr": "\u045F", + "/dzhecyrillic": "\u045F", + "/dzjekomicyr": "\u0507", + "/dzzhecyr": "\u052B", + "/e": "\u0065", + "/e-mail": "\u1F4E7", + "/e.fina": "\uFBE5", + "/e.inferior": "\u2091", + "/e.init": "\uFBE6", + "/e.isol": "\uFBE4", + "/e.medi": "\uFBE7", + "/eVfullwidth": "\u32CE", + "/eacute": "\u00E9", + "/earOfMaize": "\u1F33D", + "/earOfRice": "\u1F33E", + "/earth": "\u2641", + "/earthGlobeAmericas": "\u1F30E", + "/earthGlobeAsiaAustralia": "\u1F30F", + "/earthGlobeEuropeAfrica": "\u1F30D", + "/earthground": "\u23DA", + "/earthideographiccircled": "\u328F", + "/earthideographicparen": "\u322F", + "/eastsyriaccross": "\u2671", + "/ebengali": "\u098F", + "/ebopomofo": "\u311C", + "/ebreve": "\u0115", + "/ecandradeva": "\u090D", + "/ecandragujarati": "\u0A8D", + "/ecandravowelsigndeva": "\u0945", + "/ecandravowelsigngujarati": "\u0AC5", + "/ecaron": "\u011B", + "/ecedilla": "\u0229", + "/ecedillabreve": "\u1E1D", + "/echarmenian": "\u0565", + "/echyiwnarmenian": "\u0587", + "/ecircle": "\u24D4", + "/ecirclekatakana": "\u32D3", + "/ecircumflex": "\u00EA", + "/ecircumflexacute": "\u1EBF", + "/ecircumflexbelow": "\u1E19", + "/ecircumflexdotbelow": "\u1EC7", + "/ecircumflexgrave": "\u1EC1", + "/ecircumflexhoi": "\u1EC3", + "/ecircumflexhookabove": "\u1EC3", + "/ecircumflextilde": "\u1EC5", + "/ecyrillic": "\u0454", + "/edblgrave": "\u0205", + "/edblstruckitalic": "\u2147", + "/edeva": "\u090F", + "/edieresis": "\u00EB", + "/edot": "\u0117", + "/edotaccent": "\u0117", + "/edotbelow": "\u1EB9", + "/eegurmukhi": "\u0A0F", + "/eekaasquare": "\u3308", + "/eematragurmukhi": "\u0A47", + "/efcyr": "\u0444", + "/efcyrillic": "\u0444", + "/egrave": "\u00E8", + "/egravedbl": "\u0205", + "/egujarati": "\u0A8F", + "/egyptain": "\uA725", + "/egyptalef": "\uA723", + "/eharmenian": "\u0567", + "/ehbopomofo": "\u311D", + "/ehiragana": "\u3048", + "/ehoi": "\u1EBB", + "/ehookabove": "\u1EBB", + "/eibopomofo": "\u311F", + "/eight": "\u0038", + "/eight.inferior": "\u2088", + "/eight.roman": "\u2167", + "/eight.romansmall": "\u2177", + "/eight.superior": "\u2078", + "/eightarabic": "\u0668", + "/eightbengali": "\u09EE", + "/eightcircle": "\u2467", + "/eightcircledbl": "\u24FC", + "/eightcircleinversesansserif": "\u2791", + "/eightcomma": "\u1F109", + "/eightdeva": "\u096E", + "/eighteencircle": "\u2471", + "/eighteencircleblack": "\u24F2", + "/eighteenparen": "\u2485", + "/eighteenparenthesized": "\u2485", + "/eighteenperiod": "\u2499", + "/eightfar": "\u06F8", + "/eightgujarati": "\u0AEE", + "/eightgurmukhi": "\u0A6E", + "/eighthackarabic": "\u0668", + "/eighthangzhou": "\u3028", + "/eighthnote": "\u266A", + "/eighthnotebeamed": "\u266B", + "/eightideographiccircled": "\u3287", + "/eightideographicparen": "\u3227", + "/eightinferior": "\u2088", + "/eightksquare": "\u1F19F", + "/eightmonospace": "\uFF18", + "/eightoldstyle": "\uF738", + "/eightparen": "\u247B", + "/eightparenthesized": "\u247B", + "/eightperiod": "\u248F", + "/eightpersian": "\u06F8", + "/eightroman": "\u2177", + "/eightsuperior": "\u2078", + "/eightthai": "\u0E58", + "/eightycirclesquare": "\u324F", + "/einvertedbreve": "\u0207", + "/eiotifiedcyr": "\u0465", + "/eiotifiedcyrillic": "\u0465", + "/eject": "\u23CF", + "/ekatakana": "\u30A8", + "/ekatakanahalfwidth": "\uFF74", + "/ekonkargurmukhi": "\u0A74", + "/ekorean": "\u3154", + "/elcyr": "\u043B", + "/elcyrillic": "\u043B", + "/electricLightBulb": "\u1F4A1", + "/electricPlug": "\u1F50C", + "/electricTorch": "\u1F526", + "/electricalintersection": "\u23E7", + "/electricarrow": "\u2301", + "/element": "\u2208", + "/elementdotabove": "\u22F5", + "/elementlonghorizontalstroke": "\u22F2", + "/elementopeningup": "\u27D2", + "/elementoverbar": "\u22F6", + "/elementoverbarsmall": "\u22F7", + "/elementsmall": "\u220A", + "/elementsmallverticalbarhorizontalstroke": "\u22F4", + "/elementtwoshorizontalstroke": "\u22F9", + "/elementunderbar": "\u22F8", + "/elementverticalbarhorizontalstroke": "\u22F3", + "/elephant": "\u1F418", + "/eleven.roman": "\u216A", + "/eleven.romansmall": "\u217A", + "/elevencircle": "\u246A", + "/elevencircleblack": "\u24EB", + "/elevenparen": "\u247E", + "/elevenparenthesized": "\u247E", + "/elevenperiod": "\u2492", + "/elevenroman": "\u217A", + "/elhookcyr": "\u0513", + "/ellipsis": "\u2026", + "/ellipsisdiagonaldownright": "\u22F1", + "/ellipsisdiagonalupright": "\u22F0", + "/ellipsismidhorizontal": "\u22EF", + "/ellipsisvertical": "\u22EE", + "/elmiddlehookcyr": "\u0521", + "/elsharptailcyr": "\u04C6", + "/eltailcyr": "\u052F", + "/emacron": "\u0113", + "/emacronacute": "\u1E17", + "/emacrongrave": "\u1E15", + "/emcyr": "\u043C", + "/emcyrillic": "\u043C", + "/emdash": "\u2014", + "/emdashdbl": "\u2E3A", + "/emdashtpl": "\u2E3B", + "/emdashvertical": "\uFE31", + "/emojiModifierFitzpatrickType-1-2": "\u1F3FB", + "/emojiModifierFitzpatrickType-3": "\u1F3FC", + "/emojiModifierFitzpatrickType-4": "\u1F3FD", + "/emojiModifierFitzpatrickType-5": "\u1F3FE", + "/emojiModifierFitzpatrickType-6": "\u1F3FF", + "/emonospace": "\uFF45", + "/emphasis": "\u2383", + "/emphasismarkarmenian": "\u055B", + "/emptyDocument": "\u1F5CB", + "/emptyNote": "\u1F5C5", + "/emptyNotePad": "\u1F5C7", + "/emptyNotePage": "\u1F5C6", + "/emptyPage": "\u1F5CC", + "/emptyPages": "\u1F5CD", + "/emptyset": "\u2205", + "/emquad": "\u2001", + "/emsharptailcyr": "\u04CE", + "/emspace": "\u2003", + "/enbopomofo": "\u3123", + "/encyr": "\u043D", + "/encyrillic": "\u043D", + "/endLeftwardsArrowAbove": "\u1F51A", + "/endash": "\u2013", + "/endashvertical": "\uFE32", + "/endescendercyrillic": "\u04A3", + "/endpro": "\u220E", + "/eng": "\u014B", + "/engbopomofo": "\u3125", + "/engecyr": "\u04A5", + "/enghecyrillic": "\u04A5", + "/enhookcyr": "\u04C8", + "/enhookcyrillic": "\u04C8", + "/enhookleftcyr": "\u0529", + "/enmiddlehookcyr": "\u0523", + "/enotch": "\u2C78", + "/enquad": "\u2000", + "/ensharptailcyr": "\u04CA", + "/enspace": "\u2002", + "/entailcyr": "\u04A3", + "/enter": "\u2386", + "/enterpriseideographiccircled": "\u32AD", + "/enterpriseideographicparen": "\u323D", + "/envelopeDownwardsArrowAbove": "\u1F4E9", + "/envelopeLightning": "\u1F584", + "/eogonek": "\u0119", + "/eokorean": "\u3153", + "/eopen": "\u025B", + "/eopenclosed": "\u029A", + "/eopenreversed": "\u025C", + "/eopenreversedclosed": "\u025E", + "/eopenreversedhook": "\u025D", + "/eparen": "\u24A0", + "/eparenthesized": "\u24A0", + "/epsilon": "\u03B5", + "/epsilonacute": "\u1F73", + "/epsilonasper": "\u1F11", + "/epsilonasperacute": "\u1F15", + "/epsilonaspergrave": "\u1F13", + "/epsilongrave": "\u1F72", + "/epsilonlenis": "\u1F10", + "/epsilonlenisacute": "\u1F14", + "/epsilonlenisgrave": "\u1F12", + "/epsilonlunatesymbol": "\u03F5", + "/epsilonreversedlunatesymbol": "\u03F6", + "/epsilontonos": "\u03AD", + "/epsilonunderlinefunc": "\u2377", + "/equal": "\u003D", + "/equal.inferior": "\u208C", + "/equal.superior": "\u207C", + "/equalandparallel": "\u22D5", + "/equalbydefinition": "\u225D", + "/equalmonospace": "\uFF1D", + "/equalorgreater": "\u22DD", + "/equalorless": "\u22DC", + "/equalorprecedes": "\u22DE", + "/equalorsucceeds": "\u22DF", + "/equalscolon": "\u2255", + "/equalsmall": "\uFE66", + "/equalsuperior": "\u207C", + "/equiangular": "\u225A", + "/equivalence": "\u2261", + "/equivalent": "\u224D", + "/eranameheiseisquare": "\u337B", + "/eranamemeizisquare": "\u337E", + "/eranamesyouwasquare": "\u337C", + "/eranametaisyousquare": "\u337D", + "/eraseleft": "\u232B", + "/eraseright": "\u2326", + "/erbopomofo": "\u3126", + "/ercyr": "\u0440", + "/ercyrillic": "\u0440", + "/ereversed": "\u0258", + "/ereversedcyr": "\u044D", + "/ereversedcyrillic": "\u044D", + "/ereverseddieresiscyr": "\u04ED", + "/ergfullwidth": "\u32CD", + "/ertickcyr": "\u048F", + "/escript": "\u212F", + "/escyr": "\u0441", + "/escyrillic": "\u0441", + "/esdescendercyrillic": "\u04AB", + "/esh": "\u0283", + "/eshcurl": "\u0286", + "/eshortdeva": "\u090E", + "/eshortvowelsigndeva": "\u0946", + "/eshreversedloop": "\u01AA", + "/eshsquatreversed": "\u0285", + "/esmallhiragana": "\u3047", + "/esmallkatakana": "\u30A7", + "/esmallkatakanahalfwidth": "\uFF6A", + "/estailcyr": "\u04AB", + "/estimated": "\u212E", + "/estimates": "\u2259", + "/estroke": "\u0247", + "/esukuudosquare": "\u3307", + "/esuperior": "\uF6EC", + "/et": "\uA76B", + "/eta": "\u03B7", + "/etaacute": "\u1F75", + "/etaacuteiotasub": "\u1FC4", + "/etaasper": "\u1F21", + "/etaasperacute": "\u1F25", + "/etaasperacuteiotasub": "\u1F95", + "/etaaspergrave": "\u1F23", + "/etaaspergraveiotasub": "\u1F93", + "/etaasperiotasub": "\u1F91", + "/etaaspertilde": "\u1F27", + "/etaaspertildeiotasub": "\u1F97", + "/etagrave": "\u1F74", + "/etagraveiotasub": "\u1FC2", + "/etaiotasub": "\u1FC3", + "/etalenis": "\u1F20", + "/etalenisacute": "\u1F24", + "/etalenisacuteiotasub": "\u1F94", + "/etalenisgrave": "\u1F22", + "/etalenisgraveiotasub": "\u1F92", + "/etalenisiotasub": "\u1F90", + "/etalenistilde": "\u1F26", + "/etalenistildeiotasub": "\u1F96", + "/etarmenian": "\u0568", + "/etatilde": "\u1FC6", + "/etatildeiotasub": "\u1FC7", + "/etatonos": "\u03AE", + "/eth": "\u00F0", + "/ethi:aaglottal": "\u12A3", + "/ethi:aglottal": "\u12A0", + "/ethi:ba": "\u1260", + "/ethi:baa": "\u1263", + "/ethi:be": "\u1265", + "/ethi:bee": "\u1264", + "/ethi:bi": "\u1262", + "/ethi:bo": "\u1266", + "/ethi:bu": "\u1261", + "/ethi:bwa": "\u1267", + "/ethi:ca": "\u1278", + "/ethi:caa": "\u127B", + "/ethi:ce": "\u127D", + "/ethi:cee": "\u127C", + "/ethi:cha": "\u1328", + "/ethi:chaa": "\u132B", + "/ethi:che": "\u132D", + "/ethi:chee": "\u132C", + "/ethi:chi": "\u132A", + "/ethi:cho": "\u132E", + "/ethi:chu": "\u1329", + "/ethi:chwa": "\u132F", + "/ethi:ci": "\u127A", + "/ethi:co": "\u127E", + "/ethi:colon": "\u1365", + "/ethi:comma": "\u1363", + "/ethi:cu": "\u1279", + "/ethi:cwa": "\u127F", + "/ethi:da": "\u12F0", + "/ethi:daa": "\u12F3", + "/ethi:dda": "\u12F8", + "/ethi:ddaa": "\u12FB", + "/ethi:dde": "\u12FD", + "/ethi:ddee": "\u12FC", + "/ethi:ddi": "\u12FA", + "/ethi:ddo": "\u12FE", + "/ethi:ddu": "\u12F9", + "/ethi:ddwa": "\u12FF", + "/ethi:de": "\u12F5", + "/ethi:dee": "\u12F4", + "/ethi:di": "\u12F2", + "/ethi:do": "\u12F6", + "/ethi:du": "\u12F1", + "/ethi:dwa": "\u12F7", + "/ethi:eeglottal": "\u12A4", + "/ethi:eglottal": "\u12A5", + "/ethi:eight": "\u1370", + "/ethi:eighty": "\u1379", + "/ethi:fa": "\u1348", + "/ethi:faa": "\u134B", + "/ethi:fe": "\u134D", + "/ethi:fee": "\u134C", + "/ethi:fi": "\u134A", + "/ethi:fifty": "\u1376", + "/ethi:five": "\u136D", + "/ethi:fo": "\u134E", + "/ethi:forty": "\u1375", + "/ethi:four": "\u136C", + "/ethi:fu": "\u1349", + "/ethi:fullstop": "\u1362", + "/ethi:fwa": "\u134F", + "/ethi:fya": "\u135A", + "/ethi:ga": "\u1308", + "/ethi:gaa": "\u130B", + "/ethi:ge": "\u130D", + "/ethi:gee": "\u130C", + "/ethi:geminationandvowellengthmarkcmb": "\u135D", + "/ethi:geminationmarkcmb": "\u135F", + "/ethi:gga": "\u1318", + "/ethi:ggaa": "\u131B", + "/ethi:gge": "\u131D", + "/ethi:ggee": "\u131C", + "/ethi:ggi": "\u131A", + "/ethi:ggo": "\u131E", + "/ethi:ggu": "\u1319", + "/ethi:ggwaa": "\u131F", + "/ethi:gi": "\u130A", + "/ethi:go": "\u130E", + "/ethi:goa": "\u130F", + "/ethi:gu": "\u1309", + "/ethi:gwa": "\u1310", + "/ethi:gwaa": "\u1313", + "/ethi:gwe": "\u1315", + "/ethi:gwee": "\u1314", + "/ethi:gwi": "\u1312", + "/ethi:ha": "\u1200", + "/ethi:haa": "\u1203", + "/ethi:he": "\u1205", + "/ethi:hee": "\u1204", + "/ethi:hha": "\u1210", + "/ethi:hhaa": "\u1213", + "/ethi:hhe": "\u1215", + "/ethi:hhee": "\u1214", + "/ethi:hhi": "\u1212", + "/ethi:hho": "\u1216", + "/ethi:hhu": "\u1211", + "/ethi:hhwa": "\u1217", + "/ethi:hi": "\u1202", + "/ethi:ho": "\u1206", + "/ethi:hoa": "\u1207", + "/ethi:hu": "\u1201", + "/ethi:hundred": "\u137B", + "/ethi:iglottal": "\u12A2", + "/ethi:ja": "\u1300", + "/ethi:jaa": "\u1303", + "/ethi:je": "\u1305", + "/ethi:jee": "\u1304", + "/ethi:ji": "\u1302", + "/ethi:jo": "\u1306", + "/ethi:ju": "\u1301", + "/ethi:jwa": "\u1307", + "/ethi:ka": "\u12A8", + "/ethi:kaa": "\u12AB", + "/ethi:ke": "\u12AD", + "/ethi:kee": "\u12AC", + "/ethi:ki": "\u12AA", + "/ethi:ko": "\u12AE", + "/ethi:koa": "\u12AF", + "/ethi:ku": "\u12A9", + "/ethi:kwa": "\u12B0", + "/ethi:kwaa": "\u12B3", + "/ethi:kwe": "\u12B5", + "/ethi:kwee": "\u12B4", + "/ethi:kwi": "\u12B2", + "/ethi:kxa": "\u12B8", + "/ethi:kxaa": "\u12BB", + "/ethi:kxe": "\u12BD", + "/ethi:kxee": "\u12BC", + "/ethi:kxi": "\u12BA", + "/ethi:kxo": "\u12BE", + "/ethi:kxu": "\u12B9", + "/ethi:kxwa": "\u12C0", + "/ethi:kxwaa": "\u12C3", + "/ethi:kxwe": "\u12C5", + "/ethi:kxwee": "\u12C4", + "/ethi:kxwi": "\u12C2", + "/ethi:la": "\u1208", + "/ethi:laa": "\u120B", + "/ethi:le": "\u120D", + "/ethi:lee": "\u120C", + "/ethi:li": "\u120A", + "/ethi:lo": "\u120E", + "/ethi:lu": "\u1209", + "/ethi:lwa": "\u120F", + "/ethi:ma": "\u1218", + "/ethi:maa": "\u121B", + "/ethi:me": "\u121D", + "/ethi:mee": "\u121C", + "/ethi:mi": "\u121A", + "/ethi:mo": "\u121E", + "/ethi:mu": "\u1219", + "/ethi:mwa": "\u121F", + "/ethi:mya": "\u1359", + "/ethi:na": "\u1290", + "/ethi:naa": "\u1293", + "/ethi:ne": "\u1295", + "/ethi:nee": "\u1294", + "/ethi:ni": "\u1292", + "/ethi:nine": "\u1371", + "/ethi:ninety": "\u137A", + "/ethi:no": "\u1296", + "/ethi:nu": "\u1291", + "/ethi:nwa": "\u1297", + "/ethi:nya": "\u1298", + "/ethi:nyaa": "\u129B", + "/ethi:nye": "\u129D", + "/ethi:nyee": "\u129C", + "/ethi:nyi": "\u129A", + "/ethi:nyo": "\u129E", + "/ethi:nyu": "\u1299", + "/ethi:nywa": "\u129F", + "/ethi:oglottal": "\u12A6", + "/ethi:one": "\u1369", + "/ethi:pa": "\u1350", + "/ethi:paa": "\u1353", + "/ethi:paragraphseparator": "\u1368", + "/ethi:pe": "\u1355", + "/ethi:pee": "\u1354", + "/ethi:pha": "\u1330", + "/ethi:phaa": "\u1333", + "/ethi:pharyngeala": "\u12D0", + "/ethi:pharyngealaa": "\u12D3", + "/ethi:pharyngeale": "\u12D5", + "/ethi:pharyngealee": "\u12D4", + "/ethi:pharyngeali": "\u12D2", + "/ethi:pharyngealo": "\u12D6", + "/ethi:pharyngealu": "\u12D1", + "/ethi:phe": "\u1335", + "/ethi:phee": "\u1334", + "/ethi:phi": "\u1332", + "/ethi:pho": "\u1336", + "/ethi:phu": "\u1331", + "/ethi:phwa": "\u1337", + "/ethi:pi": "\u1352", + "/ethi:po": "\u1356", + "/ethi:prefacecolon": "\u1366", + "/ethi:pu": "\u1351", + "/ethi:pwa": "\u1357", + "/ethi:qa": "\u1240", + "/ethi:qaa": "\u1243", + "/ethi:qe": "\u1245", + "/ethi:qee": "\u1244", + "/ethi:qha": "\u1250", + "/ethi:qhaa": "\u1253", + "/ethi:qhe": "\u1255", + "/ethi:qhee": "\u1254", + "/ethi:qhi": "\u1252", + "/ethi:qho": "\u1256", + "/ethi:qhu": "\u1251", + "/ethi:qhwa": "\u1258", + "/ethi:qhwaa": "\u125B", + "/ethi:qhwe": "\u125D", + "/ethi:qhwee": "\u125C", + "/ethi:qhwi": "\u125A", + "/ethi:qi": "\u1242", + "/ethi:qo": "\u1246", + "/ethi:qoa": "\u1247", + "/ethi:qu": "\u1241", + "/ethi:questionmark": "\u1367", + "/ethi:qwa": "\u1248", + "/ethi:qwaa": "\u124B", + "/ethi:qwe": "\u124D", + "/ethi:qwee": "\u124C", + "/ethi:qwi": "\u124A", + "/ethi:ra": "\u1228", + "/ethi:raa": "\u122B", + "/ethi:re": "\u122D", + "/ethi:ree": "\u122C", + "/ethi:ri": "\u122A", + "/ethi:ro": "\u122E", + "/ethi:ru": "\u1229", + "/ethi:rwa": "\u122F", + "/ethi:rya": "\u1358", + "/ethi:sa": "\u1230", + "/ethi:saa": "\u1233", + "/ethi:se": "\u1235", + "/ethi:sectionmark": "\u1360", + "/ethi:see": "\u1234", + "/ethi:semicolon": "\u1364", + "/ethi:seven": "\u136F", + "/ethi:seventy": "\u1378", + "/ethi:sha": "\u1238", + "/ethi:shaa": "\u123B", + "/ethi:she": "\u123D", + "/ethi:shee": "\u123C", + "/ethi:shi": "\u123A", + "/ethi:sho": "\u123E", + "/ethi:shu": "\u1239", + "/ethi:shwa": "\u123F", + "/ethi:si": "\u1232", + "/ethi:six": "\u136E", + "/ethi:sixty": "\u1377", + "/ethi:so": "\u1236", + "/ethi:su": "\u1231", + "/ethi:swa": "\u1237", + "/ethi:sza": "\u1220", + "/ethi:szaa": "\u1223", + "/ethi:sze": "\u1225", + "/ethi:szee": "\u1224", + "/ethi:szi": "\u1222", + "/ethi:szo": "\u1226", + "/ethi:szu": "\u1221", + "/ethi:szwa": "\u1227", + "/ethi:ta": "\u1270", + "/ethi:taa": "\u1273", + "/ethi:te": "\u1275", + "/ethi:tee": "\u1274", + "/ethi:ten": "\u1372", + "/ethi:tenthousand": "\u137C", + "/ethi:tha": "\u1320", + "/ethi:thaa": "\u1323", + "/ethi:the": "\u1325", + "/ethi:thee": "\u1324", + "/ethi:thi": "\u1322", + "/ethi:thirty": "\u1374", + "/ethi:tho": "\u1326", + "/ethi:three": "\u136B", + "/ethi:thu": "\u1321", + "/ethi:thwa": "\u1327", + "/ethi:ti": "\u1272", + "/ethi:to": "\u1276", + "/ethi:tsa": "\u1338", + "/ethi:tsaa": "\u133B", + "/ethi:tse": "\u133D", + "/ethi:tsee": "\u133C", + "/ethi:tsi": "\u133A", + "/ethi:tso": "\u133E", + "/ethi:tsu": "\u1339", + "/ethi:tswa": "\u133F", + "/ethi:tu": "\u1271", + "/ethi:twa": "\u1277", + "/ethi:twenty": "\u1373", + "/ethi:two": "\u136A", + "/ethi:tza": "\u1340", + "/ethi:tzaa": "\u1343", + "/ethi:tze": "\u1345", + "/ethi:tzee": "\u1344", + "/ethi:tzi": "\u1342", + "/ethi:tzo": "\u1346", + "/ethi:tzoa": "\u1347", + "/ethi:tzu": "\u1341", + "/ethi:uglottal": "\u12A1", + "/ethi:va": "\u1268", + "/ethi:vaa": "\u126B", + "/ethi:ve": "\u126D", + "/ethi:vee": "\u126C", + "/ethi:vi": "\u126A", + "/ethi:vo": "\u126E", + "/ethi:vowellengthmarkcmb": "\u135E", + "/ethi:vu": "\u1269", + "/ethi:vwa": "\u126F", + "/ethi:wa": "\u12C8", + "/ethi:waa": "\u12CB", + "/ethi:waglottal": "\u12A7", + "/ethi:we": "\u12CD", + "/ethi:wee": "\u12CC", + "/ethi:wi": "\u12CA", + "/ethi:wo": "\u12CE", + "/ethi:woa": "\u12CF", + "/ethi:wordspace": "\u1361", + "/ethi:wu": "\u12C9", + "/ethi:xa": "\u1280", + "/ethi:xaa": "\u1283", + "/ethi:xe": "\u1285", + "/ethi:xee": "\u1284", + "/ethi:xi": "\u1282", + "/ethi:xo": "\u1286", + "/ethi:xoa": "\u1287", + "/ethi:xu": "\u1281", + "/ethi:xwa": "\u1288", + "/ethi:xwaa": "\u128B", + "/ethi:xwe": "\u128D", + "/ethi:xwee": "\u128C", + "/ethi:xwi": "\u128A", + "/ethi:ya": "\u12E8", + "/ethi:yaa": "\u12EB", + "/ethi:ye": "\u12ED", + "/ethi:yee": "\u12EC", + "/ethi:yi": "\u12EA", + "/ethi:yo": "\u12EE", + "/ethi:yoa": "\u12EF", + "/ethi:yu": "\u12E9", + "/ethi:za": "\u12D8", + "/ethi:zaa": "\u12DB", + "/ethi:ze": "\u12DD", + "/ethi:zee": "\u12DC", + "/ethi:zha": "\u12E0", + "/ethi:zhaa": "\u12E3", + "/ethi:zhe": "\u12E5", + "/ethi:zhee": "\u12E4", + "/ethi:zhi": "\u12E2", + "/ethi:zho": "\u12E6", + "/ethi:zhu": "\u12E1", + "/ethi:zhwa": "\u12E7", + "/ethi:zi": "\u12DA", + "/ethi:zo": "\u12DE", + "/ethi:zu": "\u12D9", + "/ethi:zwa": "\u12DF", + "/etilde": "\u1EBD", + "/etildebelow": "\u1E1B", + "/etnahta:hb": "\u0591", + "/etnahtafoukhhebrew": "\u0591", + "/etnahtafoukhlefthebrew": "\u0591", + "/etnahtahebrew": "\u0591", + "/etnahtalefthebrew": "\u0591", + "/eturned": "\u01DD", + "/eukorean": "\u3161", + "/eukrcyr": "\u0454", + "/euler": "\u2107", + "/euro": "\u20AC", + "/euroarchaic": "\u20A0", + "/europeanCastle": "\u1F3F0", + "/europeanPostOffice": "\u1F3E4", + "/evergreenTree": "\u1F332", + "/evowelsignbengali": "\u09C7", + "/evowelsigndeva": "\u0947", + "/evowelsigngujarati": "\u0AC7", + "/excellentideographiccircled": "\u329D", + "/excess": "\u2239", + "/exclam": "\u0021", + "/exclamarmenian": "\u055C", + "/exclamationquestion": "\u2049", + "/exclamdbl": "\u203C", + "/exclamdown": "\u00A1", + "/exclamdownsmall": "\uF7A1", + "/exclammonospace": "\uFF01", + "/exclamsmall": "\uF721", + "/existential": "\u2203", + "/expressionlessFace": "\u1F611", + "/extraterrestrialAlien": "\u1F47D", + "/eye": "\u1F441", + "/eyeglasses": "\u1F453", + "/eyes": "\u1F440", + "/ezh": "\u0292", + "/ezhcaron": "\u01EF", + "/ezhcurl": "\u0293", + "/ezhreversed": "\u01B9", + "/ezhtail": "\u01BA", + "/f": "\u0066", + "/f_f": "\uFB00", + "/f_f_i": "\uFB03", + "/f_f_l": "\uFB04", + "/faceMassage": "\u1F486", + "/faceSavouringDeliciousFood": "\u1F60B", + "/faceScreamingInFear": "\u1F631", + "/faceThrowingAKiss": "\u1F618", + "/faceWithColdSweat": "\u1F613", + "/faceWithLookOfTriumph": "\u1F624", + "/faceWithMedicalMask": "\u1F637", + "/faceWithNoGoodGesture": "\u1F645", + "/faceWithOkGesture": "\u1F646", + "/faceWithOpenMouth": "\u1F62E", + "/faceWithOpenMouthAndColdSweat": "\u1F630", + "/faceWithRollingEyes": "\u1F644", + "/faceWithStuckOutTongue": "\u1F61B", + "/faceWithStuckOutTongueAndTightlyClosedEyes": "\u1F61D", + "/faceWithStuckOutTongueAndWinkingEye": "\u1F61C", + "/faceWithTearsOfJoy": "\u1F602", + "/faceWithoutMouth": "\u1F636", + "/facsimile": "\u213B", + "/factory": "\u1F3ED", + "/fadeva": "\u095E", + "/fagurmukhi": "\u0A5E", + "/fahrenheit": "\u2109", + "/fallenLeaf": "\u1F342", + "/fallingdiagonal": "\u27CD", + "/fallingdiagonalincircleinsquareblackwhite": "\u26DE", + "/family": "\u1F46A", + "/farsi": "\u262B", + "/farsiYehDigitFourBelow": "\u0777", + "/farsiYehDigitThreeAbove": "\u0776", + "/farsiYehDigitTwoAbove": "\u0775", + "/fatha": "\u064E", + "/fathaIsol": "\uFE76", + "/fathaMedi": "\uFE77", + "/fathaarabic": "\u064E", + "/fathalowarabic": "\u064E", + "/fathasmall": "\u0618", + "/fathatan": "\u064B", + "/fathatanIsol": "\uFE70", + "/fathatanarabic": "\u064B", + "/fathatwodotsdots": "\u065E", + "/fatherChristmas": "\u1F385", + "/faxIcon": "\u1F5B7", + "/faxMachine": "\u1F4E0", + "/fbopomofo": "\u3108", + "/fcircle": "\u24D5", + "/fdot": "\u1E1F", + "/fdotaccent": "\u1E1F", + "/fearfulFace": "\u1F628", + "/februarytelegraph": "\u32C1", + "/feh.fina": "\uFED2", + "/feh.init": "\uFED3", + "/feh.init_alefmaksura.fina": "\uFC31", + "/feh.init_hah.fina": "\uFC2E", + "/feh.init_hah.medi": "\uFCBF", + "/feh.init_jeem.fina": "\uFC2D", + "/feh.init_jeem.medi": "\uFCBE", + "/feh.init_khah.fina": "\uFC2F", + "/feh.init_khah.medi": "\uFCC0", + "/feh.init_khah.medi_meem.medi": "\uFD7D", + "/feh.init_meem.fina": "\uFC30", + "/feh.init_meem.medi": "\uFCC1", + "/feh.init_yeh.fina": "\uFC32", + "/feh.isol": "\uFED1", + "/feh.medi": "\uFED4", + "/feh.medi_alefmaksura.fina": "\uFC7C", + "/feh.medi_khah.medi_meem.fina": "\uFD7C", + "/feh.medi_meem.medi_yeh.fina": "\uFDC1", + "/feh.medi_yeh.fina": "\uFC7D", + "/fehThreeDotsUpBelow": "\u0761", + "/fehTwoDotsBelow": "\u0760", + "/feharabic": "\u0641", + "/feharmenian": "\u0586", + "/fehdotbelow": "\u06A3", + "/fehdotbelowright": "\u06A2", + "/fehfinalarabic": "\uFED2", + "/fehinitialarabic": "\uFED3", + "/fehmedialarabic": "\uFED4", + "/fehthreedotsbelow": "\u06A5", + "/feicoptic": "\u03E5", + "/female": "\u2640", + "/femaleideographiccircled": "\u329B", + "/feng": "\u02A9", + "/ferrisWheel": "\u1F3A1", + "/ferry": "\u26F4", + "/festivalideographicparen": "\u3240", + "/ff": "\uFB00", + "/ffi": "\uFB03", + "/ffl": "\uFB04", + "/fhook": "\u0192", + "/fi": "\uFB01", # ligature "fi" + "/fieldHockeyStickAndBall": "\u1F3D1", + "/fifteencircle": "\u246E", + "/fifteencircleblack": "\u24EF", + "/fifteenparen": "\u2482", + "/fifteenparenthesized": "\u2482", + "/fifteenperiod": "\u2496", + "/fifty.roman": "\u216C", + "/fifty.romansmall": "\u217C", + "/fiftycircle": "\u32BF", + "/fiftycirclesquare": "\u324C", + "/fiftyearlyform.roman": "\u2186", + "/fiftythousand.roman": "\u2187", + "/figuredash": "\u2012", + "/figurespace": "\u2007", + "/fileCabinet": "\u1F5C4", + "/fileFolder": "\u1F4C1", + "/filledbox": "\u25A0", + "/filledrect": "\u25AC", + "/filledstopabove": "\u06EC", + "/filmFrames": "\u1F39E", + "/filmProjector": "\u1F4FD", + "/finalkaf": "\u05DA", + "/finalkaf:hb": "\u05DA", + "/finalkafdagesh": "\uFB3A", + "/finalkafdageshhebrew": "\uFB3A", + "/finalkafhebrew": "\u05DA", + "/finalkafqamats": "\u05DA", + "/finalkafqamatshebrew": "\u05DA", + "/finalkafsheva": "\u05DA", + "/finalkafshevahebrew": "\u05DA", + "/finalkafwithdagesh:hb": "\uFB3A", + "/finalmem": "\u05DD", + "/finalmem:hb": "\u05DD", + "/finalmemhebrew": "\u05DD", + "/finalmemwide:hb": "\uFB26", + "/finalnun": "\u05DF", + "/finalnun:hb": "\u05DF", + "/finalnunhebrew": "\u05DF", + "/finalpe": "\u05E3", + "/finalpe:hb": "\u05E3", + "/finalpehebrew": "\u05E3", + "/finalpewithdagesh:hb": "\uFB43", + "/finalsigma": "\u03C2", + "/finaltsadi": "\u05E5", + "/finaltsadi:hb": "\u05E5", + "/finaltsadihebrew": "\u05E5", + "/financialideographiccircled": "\u3296", + "/financialideographicparen": "\u3236", + "/finsular": "\uA77C", + "/fire": "\u1F525", + "/fireEngine": "\u1F692", + "/fireideographiccircled": "\u328B", + "/fireideographicparen": "\u322B", + "/fireworkSparkler": "\u1F387", + "/fireworks": "\u1F386", + "/firstQuarterMoon": "\u1F313", + "/firstQuarterMoonFace": "\u1F31B", + "/firstquartermoon": "\u263D", + "/firststrongisolate": "\u2068", + "/firsttonechinese": "\u02C9", + "/fish": "\u1F41F", + "/fishCakeSwirlDesign": "\u1F365", + "/fisheye": "\u25C9", + "/fishingPoleAndFish": "\u1F3A3", + "/fistedHandSign": "\u1F44A", + "/fitacyr": "\u0473", + "/fitacyrillic": "\u0473", + "/five": "\u0035", + "/five.inferior": "\u2085", + "/five.roman": "\u2164", + "/five.romansmall": "\u2174", + "/five.superior": "\u2075", + "/fivearabic": "\u0665", + "/fivebengali": "\u09EB", + "/fivecircle": "\u2464", + "/fivecircledbl": "\u24F9", + "/fivecircleinversesansserif": "\u278E", + "/fivecomma": "\u1F106", + "/fivedeva": "\u096B", + "/fivedot": "\u2E2D", + "/fivedotpunctuation": "\u2059", + "/fiveeighths": "\u215D", + "/fivefar": "\u06F5", + "/fivegujarati": "\u0AEB", + "/fivegurmukhi": "\u0A6B", + "/fivehackarabic": "\u0665", + "/fivehangzhou": "\u3025", + "/fivehundred.roman": "\u216E", + "/fivehundred.romansmall": "\u217E", + "/fiveideographiccircled": "\u3284", + "/fiveideographicparen": "\u3224", + "/fiveinferior": "\u2085", + "/fivemonospace": "\uFF15", + "/fiveoldstyle": "\uF735", + "/fiveparen": "\u2478", + "/fiveparenthesized": "\u2478", + "/fiveperiod": "\u248C", + "/fivepersian": "\u06F5", + "/fivepointedstar": "\u066D", + "/fivepointonesquare": "\u1F1A0", + "/fiveroman": "\u2174", + "/fivesixths": "\u215A", + "/fivesuperior": "\u2075", + "/fivethai": "\u0E55", + "/fivethousand.roman": "\u2181", + "/fl": "\uFB02", + "/flagblack": "\u2691", + "/flaghorizontalmiddlestripeblackwhite": "\u26FF", + "/flaginhole": "\u26F3", + "/flagwhite": "\u2690", + "/flatness": "\u23E5", + "/fleurdelis": "\u269C", + "/flexedBiceps": "\u1F4AA", + "/floorleft": "\u230A", + "/floorright": "\u230B", + "/floppyDisk": "\u1F4BE", + "/floralheartbulletreversedrotated": "\u2619", + "/florin": "\u0192", + "/flower": "\u2698", + "/flowerPlayingCards": "\u1F3B4", + "/flowerpunctuationmark": "\u2055", + "/flushedFace": "\u1F633", + "/flyingEnvelope": "\u1F585", + "/flyingSaucer": "\u1F6F8", + "/fmfullwidth": "\u3399", + "/fmonospace": "\uFF46", + "/fmsquare": "\u3399", + "/fofanthai": "\u0E1F", + "/fofathai": "\u0E1D", + "/fog": "\u1F32B", + "/foggy": "\u1F301", + "/folder": "\u1F5C0", + "/fongmanthai": "\u0E4F", + "/footnote": "\u0602", + "/footprints": "\u1F463", + "/footsquare": "\u23CD", + "/forall": "\u2200", + "/forces": "\u22A9", + "/fork": "\u2442", + "/forkKnife": "\u1F374", + "/forkKnifePlate": "\u1F37D", + "/forsamaritan": "\u214F", + "/fortycircle": "\u32B5", + "/fortycirclesquare": "\u324B", + "/fortyeightcircle": "\u32BD", + "/fortyfivecircle": "\u32BA", + "/fortyfourcircle": "\u32B9", + "/fortyninecircle": "\u32BE", + "/fortyonecircle": "\u32B6", + "/fortysevencircle": "\u32BC", + "/fortysixcircle": "\u32BB", + "/fortythreecircle": "\u32B8", + "/fortytwocircle": "\u32B7", + "/fountain": "\u26F2", + "/four": "\u0034", + "/four.inferior": "\u2084", + "/four.roman": "\u2163", + "/four.romansmall": "\u2173", + "/four.superior": "\u2074", + "/fourLeafClover": "\u1F340", + "/fourarabic": "\u0664", + "/fourbengali": "\u09EA", + "/fourcircle": "\u2463", + "/fourcircledbl": "\u24F8", + "/fourcircleinversesansserif": "\u278D", + "/fourcomma": "\u1F105", + "/fourdeva": "\u096A", + "/fourdotmark": "\u205B", + "/fourdotpunctuation": "\u2058", + "/fourfar": "\u06F4", + "/fourfifths": "\u2158", + "/fourgujarati": "\u0AEA", + "/fourgurmukhi": "\u0A6A", + "/fourhackarabic": "\u0664", + "/fourhangzhou": "\u3024", + "/fourideographiccircled": "\u3283", + "/fourideographicparen": "\u3223", + "/fourinferior": "\u2084", + "/fourksquare": "\u1F19E", + "/fourmonospace": "\uFF14", + "/fournumeratorbengali": "\u09F7", + "/fouroldstyle": "\uF734", + "/fourparen": "\u2477", + "/fourparenthesized": "\u2477", + "/fourperemspace": "\u2005", + "/fourperiod": "\u248B", + "/fourpersian": "\u06F4", + "/fourroman": "\u2173", + "/foursuperior": "\u2074", + "/fourteencircle": "\u246D", + "/fourteencircleblack": "\u24EE", + "/fourteenparen": "\u2481", + "/fourteenparenthesized": "\u2481", + "/fourteenperiod": "\u2495", + "/fourthai": "\u0E54", + "/fourthtonechinese": "\u02CB", + "/fparen": "\u24A1", + "/fparenthesized": "\u24A1", + "/fraction": "\u2044", + "/frameAnX": "\u1F5BE", + "/framePicture": "\u1F5BC", + "/frameTiles": "\u1F5BD", + "/franc": "\u20A3", + "/freesquare": "\u1F193", + "/frenchFries": "\u1F35F", + "/freversedepigraphic": "\uA7FB", + "/friedShrimp": "\u1F364", + "/frogFace": "\u1F438", + "/front-facingBabyChick": "\u1F425", + "/frown": "\u2322", + "/frowningFaceWithOpenMouth": "\u1F626", + "/frowningfacewhite": "\u2639", + "/fstroke": "\uA799", + "/fturned": "\u214E", + "/fuelpump": "\u26FD", + "/fullBlock": "\u2588", + "/fullMoon": "\u1F315", + "/fullMoonFace": "\u1F31D", + "/functionapplication": "\u2061", + "/funeralurn": "\u26B1", + "/fuse": "\u23DB", + "/fwd:A": "\uFF21", + "/fwd:B": "\uFF22", + "/fwd:C": "\uFF23", + "/fwd:D": "\uFF24", + "/fwd:E": "\uFF25", + "/fwd:F": "\uFF26", + "/fwd:G": "\uFF27", + "/fwd:H": "\uFF28", + "/fwd:I": "\uFF29", + "/fwd:J": "\uFF2A", + "/fwd:K": "\uFF2B", + "/fwd:L": "\uFF2C", + "/fwd:M": "\uFF2D", + "/fwd:N": "\uFF2E", + "/fwd:O": "\uFF2F", + "/fwd:P": "\uFF30", + "/fwd:Q": "\uFF31", + "/fwd:R": "\uFF32", + "/fwd:S": "\uFF33", + "/fwd:T": "\uFF34", + "/fwd:U": "\uFF35", + "/fwd:V": "\uFF36", + "/fwd:W": "\uFF37", + "/fwd:X": "\uFF38", + "/fwd:Y": "\uFF39", + "/fwd:Z": "\uFF3A", + "/fwd:a": "\uFF41", + "/fwd:ampersand": "\uFF06", + "/fwd:asciicircum": "\uFF3E", + "/fwd:asciitilde": "\uFF5E", + "/fwd:asterisk": "\uFF0A", + "/fwd:at": "\uFF20", + "/fwd:b": "\uFF42", + "/fwd:backslash": "\uFF3C", + "/fwd:bar": "\uFF5C", + "/fwd:braceleft": "\uFF5B", + "/fwd:braceright": "\uFF5D", + "/fwd:bracketleft": "\uFF3B", + "/fwd:bracketright": "\uFF3D", + "/fwd:brokenbar": "\uFFE4", + "/fwd:c": "\uFF43", + "/fwd:centsign": "\uFFE0", + "/fwd:colon": "\uFF1A", + "/fwd:comma": "\uFF0C", + "/fwd:d": "\uFF44", + "/fwd:dollar": "\uFF04", + "/fwd:e": "\uFF45", + "/fwd:eight": "\uFF18", + "/fwd:equal": "\uFF1D", + "/fwd:exclam": "\uFF01", + "/fwd:f": "\uFF46", + "/fwd:five": "\uFF15", + "/fwd:four": "\uFF14", + "/fwd:g": "\uFF47", + "/fwd:grave": "\uFF40", + "/fwd:greater": "\uFF1E", + "/fwd:h": "\uFF48", + "/fwd:hyphen": "\uFF0D", + "/fwd:i": "\uFF49", + "/fwd:j": "\uFF4A", + "/fwd:k": "\uFF4B", + "/fwd:l": "\uFF4C", + "/fwd:leftwhiteparenthesis": "\uFF5F", + "/fwd:less": "\uFF1C", + "/fwd:m": "\uFF4D", + "/fwd:macron": "\uFFE3", + "/fwd:n": "\uFF4E", + "/fwd:nine": "\uFF19", + "/fwd:notsign": "\uFFE2", + "/fwd:numbersign": "\uFF03", + "/fwd:o": "\uFF4F", + "/fwd:one": "\uFF11", + "/fwd:p": "\uFF50", + "/fwd:parenthesisleft": "\uFF08", + "/fwd:parenthesisright": "\uFF09", + "/fwd:percent": "\uFF05", + "/fwd:period": "\uFF0E", + "/fwd:plus": "\uFF0B", + "/fwd:poundsign": "\uFFE1", + "/fwd:q": "\uFF51", + "/fwd:question": "\uFF1F", + "/fwd:quotedbl": "\uFF02", + "/fwd:quotesingle": "\uFF07", + "/fwd:r": "\uFF52", + "/fwd:rightwhiteparenthesis": "\uFF60", + "/fwd:s": "\uFF53", + "/fwd:semicolon": "\uFF1B", + "/fwd:seven": "\uFF17", + "/fwd:six": "\uFF16", + "/fwd:slash": "\uFF0F", + "/fwd:t": "\uFF54", + "/fwd:three": "\uFF13", + "/fwd:two": "\uFF12", + "/fwd:u": "\uFF55", + "/fwd:underscore": "\uFF3F", + "/fwd:v": "\uFF56", + "/fwd:w": "\uFF57", + "/fwd:wonsign": "\uFFE6", + "/fwd:x": "\uFF58", + "/fwd:y": "\uFF59", + "/fwd:yensign": "\uFFE5", + "/fwd:z": "\uFF5A", + "/fwd:zero": "\uFF10", + "/g": "\u0067", + "/gabengali": "\u0997", + "/gacute": "\u01F5", + "/gadeva": "\u0917", + "/gaf": "\u06AF", + "/gaf.fina": "\uFB93", + "/gaf.init": "\uFB94", + "/gaf.isol": "\uFB92", + "/gaf.medi": "\uFB95", + "/gafarabic": "\u06AF", + "/gaffinalarabic": "\uFB93", + "/gafinitialarabic": "\uFB94", + "/gafmedialarabic": "\uFB95", + "/gafring": "\u06B0", + "/gafthreedotsabove": "\u06B4", + "/gaftwodotsbelow": "\u06B2", + "/gagujarati": "\u0A97", + "/gagurmukhi": "\u0A17", + "/gahiragana": "\u304C", + "/gakatakana": "\u30AC", + "/galsquare": "\u33FF", + "/gameDie": "\u1F3B2", + "/gamma": "\u03B3", + "/gammadblstruck": "\u213D", + "/gammalatinsmall": "\u0263", + "/gammasuperior": "\u02E0", + "/gammasupmod": "\u02E0", + "/gamurda": "\uA993", + "/gangiacoptic": "\u03EB", + "/ganmasquare": "\u330F", + "/garonsquare": "\u330E", + "/gbfullwidth": "\u3387", + "/gbopomofo": "\u310D", + "/gbreve": "\u011F", + "/gcaron": "\u01E7", + "/gcedilla": "\u0123", + "/gcircle": "\u24D6", + "/gcircumflex": "\u011D", + "/gcommaaccent": "\u0123", + "/gdot": "\u0121", + "/gdotaccent": "\u0121", + "/gear": "\u2699", + "/gearhles": "\u26EE", + "/gearouthub": "\u26ED", + "/gecyr": "\u0433", + "/gecyrillic": "\u0433", + "/gehiragana": "\u3052", + "/gehookcyr": "\u0495", + "/gehookstrokecyr": "\u04FB", + "/gekatakana": "\u30B2", + "/gemStone": "\u1F48E", + "/gemini": "\u264A", + "/geometricallyequal": "\u2251", + "/geometricallyequivalent": "\u224E", + "/geometricproportion": "\u223A", + "/geresh:hb": "\u05F3", + "/gereshMuqdam:hb": "\u059D", + "/gereshaccenthebrew": "\u059C", + "/gereshhebrew": "\u05F3", + "/gereshmuqdamhebrew": "\u059D", + "/germandbls": "\u00DF", + "/germanpenny": "\u20B0", + "/gershayim:hb": "\u05F4", + "/gershayimaccenthebrew": "\u059E", + "/gershayimhebrew": "\u05F4", + "/gestrokecyr": "\u0493", + "/getailcyr": "\u04F7", + "/getamark": "\u3013", + "/geupcyr": "\u0491", + "/ghabengali": "\u0998", + "/ghadarmenian": "\u0572", + "/ghadeva": "\u0918", + "/ghagujarati": "\u0A98", + "/ghagurmukhi": "\u0A18", + "/ghain": "\u063A", + "/ghain.fina": "\uFECE", + "/ghain.init": "\uFECF", + "/ghain.init_alefmaksura.fina": "\uFCF9", + "/ghain.init_jeem.fina": "\uFC2B", + "/ghain.init_jeem.medi": "\uFCBC", + "/ghain.init_meem.fina": "\uFC2C", + "/ghain.init_meem.medi": "\uFCBD", + "/ghain.init_yeh.fina": "\uFCFA", + "/ghain.isol": "\uFECD", + "/ghain.medi": "\uFED0", + "/ghain.medi_alefmaksura.fina": "\uFD15", + "/ghain.medi_meem.medi_alefmaksura.fina": "\uFD7B", + "/ghain.medi_meem.medi_meem.fina": "\uFD79", + "/ghain.medi_meem.medi_yeh.fina": "\uFD7A", + "/ghain.medi_yeh.fina": "\uFD16", + "/ghainarabic": "\u063A", + "/ghaindotbelow": "\u06FC", + "/ghainfinalarabic": "\uFECE", + "/ghaininitialarabic": "\uFECF", + "/ghainmedialarabic": "\uFED0", + "/ghemiddlehookcyrillic": "\u0495", + "/ghestrokecyrillic": "\u0493", + "/gheupturncyrillic": "\u0491", + "/ghhadeva": "\u095A", + "/ghhagurmukhi": "\u0A5A", + "/ghook": "\u0260", + "/ghost": "\u1F47B", + "/ghzfullwidth": "\u3393", + "/ghzsquare": "\u3393", + "/gigasquare": "\u3310", + "/gihiragana": "\u304E", + "/gikatakana": "\u30AE", + "/gimarmenian": "\u0563", + "/gimel": "\u05D2", + "/gimel:hb": "\u05D2", + "/gimeldagesh": "\uFB32", + "/gimeldageshhebrew": "\uFB32", + "/gimelhebrew": "\u05D2", + "/gimelwithdagesh:hb": "\uFB32", + "/giniisquare": "\u3311", + "/ginsularturned": "\uA77F", + "/girl": "\u1F467", + "/girls": "\u1F6CA", + "/girudaasquare": "\u3313", + "/gjecyr": "\u0453", + "/gjecyrillic": "\u0453", + "/globeMeridians": "\u1F310", + "/glottalinvertedstroke": "\u01BE", + "/glottalstop": "\u0294", + "/glottalstopinverted": "\u0296", + "/glottalstopmod": "\u02C0", + "/glottalstopreversed": "\u0295", + "/glottalstopreversedmod": "\u02C1", + "/glottalstopreversedsuperior": "\u02E4", + "/glottalstopstroke": "\u02A1", + "/glottalstopstrokereversed": "\u02A2", + "/glottalstopsupreversedmod": "\u02E4", + "/glowingStar": "\u1F31F", + "/gmacron": "\u1E21", + "/gmonospace": "\uFF47", + "/gmtr:diamondblack": "\u25C6", + "/gmtr:diamondwhite": "\u25C7", + "/gnrl:hyphen": "\u2010", + "/goat": "\u1F410", + "/gobliquestroke": "\uA7A1", + "/gohiragana": "\u3054", + "/gokatakana": "\u30B4", + "/golfer": "\u1F3CC", + "/gpafullwidth": "\u33AC", + "/gparen": "\u24A2", + "/gparenthesized": "\u24A2", + "/gpasquare": "\u33AC", + "/gr:acute": "\u1FFD", + "/gr:grave": "\u1FEF", + "/gr:question": "\u037E", + "/gr:tilde": "\u1FC0", + "/gradient": "\u2207", + "/graduationCap": "\u1F393", + "/grapes": "\u1F347", + "/grave": "\u0060", + "/gravebelowcmb": "\u0316", + "/gravecmb": "\u0300", + "/gravecomb": "\u0300", + "/gravedblmiddlemod": "\u02F5", + "/gravedeva": "\u0953", + "/gravelowmod": "\u02CE", + "/gravemiddlemod": "\u02F4", + "/gravemod": "\u02CB", + "/gravemonospace": "\uFF40", + "/gravetonecmb": "\u0340", + "/greater": "\u003E", + "/greaterbutnotequal": "\u2269", + "/greaterbutnotequivalent": "\u22E7", + "/greaterdot": "\u22D7", + "/greaterequal": "\u2265", + "/greaterequalorless": "\u22DB", + "/greatermonospace": "\uFF1E", + "/greaterorequivalent": "\u2273", + "/greaterorless": "\u2277", + "/greateroverequal": "\u2267", + "/greatersmall": "\uFE65", + "/greenApple": "\u1F34F", + "/greenBook": "\u1F4D7", + "/greenHeart": "\u1F49A", + "/grimacingFace": "\u1F62C", + "/grinningCatFaceWithSmilingEyes": "\u1F638", + "/grinningFace": "\u1F600", + "/grinningFaceWithSmilingEyes": "\u1F601", + "/growingHeart": "\u1F497", + "/gscript": "\u0261", + "/gstroke": "\u01E5", + "/guarani": "\u20B2", + "/guardsman": "\u1F482", + "/gueh": "\u06B3", + "/gueh.fina": "\uFB97", + "/gueh.init": "\uFB98", + "/gueh.isol": "\uFB96", + "/gueh.medi": "\uFB99", + "/guhiragana": "\u3050", + "/guillemetleft": "\u00AB", + "/guillemetright": "\u00BB", + "/guillemotleft": "\u00AB", + "/guillemotright": "\u00BB", + "/guilsinglleft": "\u2039", + "/guilsinglright": "\u203A", + "/guitar": "\u1F3B8", + "/gujr:a": "\u0A85", + "/gujr:aa": "\u0A86", + "/gujr:aasign": "\u0ABE", + "/gujr:abbreviation": "\u0AF0", + "/gujr:ai": "\u0A90", + "/gujr:aisign": "\u0AC8", + "/gujr:anusvara": "\u0A82", + "/gujr:au": "\u0A94", + "/gujr:ausign": "\u0ACC", + "/gujr:avagraha": "\u0ABD", + "/gujr:ba": "\u0AAC", + "/gujr:bha": "\u0AAD", + "/gujr:binducandra": "\u0A81", + "/gujr:ca": "\u0A9A", + "/gujr:cha": "\u0A9B", + "/gujr:circlenuktaabove": "\u0AFE", + "/gujr:da": "\u0AA6", + "/gujr:dda": "\u0AA1", + "/gujr:ddha": "\u0AA2", + "/gujr:dha": "\u0AA7", + "/gujr:e": "\u0A8F", + "/gujr:ecandra": "\u0A8D", + "/gujr:eight": "\u0AEE", + "/gujr:esign": "\u0AC7", + "/gujr:esigncandra": "\u0AC5", + "/gujr:five": "\u0AEB", + "/gujr:four": "\u0AEA", + "/gujr:ga": "\u0A97", + "/gujr:gha": "\u0A98", + "/gujr:ha": "\u0AB9", + "/gujr:i": "\u0A87", + "/gujr:ii": "\u0A88", + "/gujr:iisign": "\u0AC0", + "/gujr:isign": "\u0ABF", + "/gujr:ja": "\u0A9C", + "/gujr:jha": "\u0A9D", + "/gujr:ka": "\u0A95", + "/gujr:kha": "\u0A96", + "/gujr:la": "\u0AB2", + "/gujr:lla": "\u0AB3", + "/gujr:llvocal": "\u0AE1", + "/gujr:llvocalsign": "\u0AE3", + "/gujr:lvocal": "\u0A8C", + "/gujr:lvocalsign": "\u0AE2", + "/gujr:ma": "\u0AAE", + "/gujr:maddah": "\u0AFC", + "/gujr:na": "\u0AA8", + "/gujr:nga": "\u0A99", + "/gujr:nine": "\u0AEF", + "/gujr:nna": "\u0AA3", + "/gujr:nukta": "\u0ABC", + "/gujr:nya": "\u0A9E", + "/gujr:o": "\u0A93", + "/gujr:ocandra": "\u0A91", + "/gujr:om": "\u0AD0", + "/gujr:one": "\u0AE7", + "/gujr:osign": "\u0ACB", + "/gujr:osigncandra": "\u0AC9", + "/gujr:pa": "\u0AAA", + "/gujr:pha": "\u0AAB", + "/gujr:ra": "\u0AB0", + "/gujr:rrvocal": "\u0AE0", + "/gujr:rrvocalsign": "\u0AC4", + "/gujr:rupee": "\u0AF1", + "/gujr:rvocal": "\u0A8B", + "/gujr:rvocalsign": "\u0AC3", + "/gujr:sa": "\u0AB8", + "/gujr:seven": "\u0AED", + "/gujr:sha": "\u0AB6", + "/gujr:shadda": "\u0AFB", + "/gujr:six": "\u0AEC", + "/gujr:ssa": "\u0AB7", + "/gujr:sukun": "\u0AFA", + "/gujr:ta": "\u0AA4", + "/gujr:tha": "\u0AA5", + "/gujr:three": "\u0AE9", + "/gujr:three-dotnuktaabove": "\u0AFD", + "/gujr:tta": "\u0A9F", + "/gujr:ttha": "\u0AA0", + "/gujr:two": "\u0AE8", + "/gujr:two-circlenuktaabove": "\u0AFF", + "/gujr:u": "\u0A89", + "/gujr:usign": "\u0AC1", + "/gujr:uu": "\u0A8A", + "/gujr:uusign": "\u0AC2", + "/gujr:va": "\u0AB5", + "/gujr:virama": "\u0ACD", + "/gujr:visarga": "\u0A83", + "/gujr:ya": "\u0AAF", + "/gujr:zero": "\u0AE6", + "/gujr:zha": "\u0AF9", + "/gukatakana": "\u30B0", + "/guramusquare": "\u3318", + "/guramutonsquare": "\u3319", + "/guru:a": "\u0A05", + "/guru:aa": "\u0A06", + "/guru:aasign": "\u0A3E", + "/guru:adakbindisign": "\u0A01", + "/guru:addak": "\u0A71", + "/guru:ai": "\u0A10", + "/guru:aisign": "\u0A48", + "/guru:au": "\u0A14", + "/guru:ausign": "\u0A4C", + "/guru:ba": "\u0A2C", + "/guru:bha": "\u0A2D", + "/guru:bindisign": "\u0A02", + "/guru:ca": "\u0A1A", + "/guru:cha": "\u0A1B", + "/guru:da": "\u0A26", + "/guru:dda": "\u0A21", + "/guru:ddha": "\u0A22", + "/guru:dha": "\u0A27", + "/guru:ee": "\u0A0F", + "/guru:eesign": "\u0A47", + "/guru:eight": "\u0A6E", + "/guru:ekonkar": "\u0A74", + "/guru:fa": "\u0A5E", + "/guru:five": "\u0A6B", + "/guru:four": "\u0A6A", + "/guru:ga": "\u0A17", + "/guru:gha": "\u0A18", + "/guru:ghha": "\u0A5A", + "/guru:ha": "\u0A39", + "/guru:i": "\u0A07", + "/guru:ii": "\u0A08", + "/guru:iisign": "\u0A40", + "/guru:iri": "\u0A72", + "/guru:isign": "\u0A3F", + "/guru:ja": "\u0A1C", + "/guru:jha": "\u0A1D", + "/guru:ka": "\u0A15", + "/guru:kha": "\u0A16", + "/guru:khha": "\u0A59", + "/guru:la": "\u0A32", + "/guru:lla": "\u0A33", + "/guru:ma": "\u0A2E", + "/guru:na": "\u0A28", + "/guru:nga": "\u0A19", + "/guru:nine": "\u0A6F", + "/guru:nna": "\u0A23", + "/guru:nukta": "\u0A3C", + "/guru:nya": "\u0A1E", + "/guru:one": "\u0A67", + "/guru:oo": "\u0A13", + "/guru:oosign": "\u0A4B", + "/guru:pa": "\u0A2A", + "/guru:pha": "\u0A2B", + "/guru:ra": "\u0A30", + "/guru:rra": "\u0A5C", + "/guru:sa": "\u0A38", + "/guru:seven": "\u0A6D", + "/guru:sha": "\u0A36", + "/guru:six": "\u0A6C", + "/guru:ta": "\u0A24", + "/guru:tha": "\u0A25", + "/guru:three": "\u0A69", + "/guru:tippi": "\u0A70", + "/guru:tta": "\u0A1F", + "/guru:ttha": "\u0A20", + "/guru:two": "\u0A68", + "/guru:u": "\u0A09", + "/guru:udaatsign": "\u0A51", + "/guru:ura": "\u0A73", + "/guru:usign": "\u0A41", + "/guru:uu": "\u0A0A", + "/guru:uusign": "\u0A42", + "/guru:va": "\u0A35", + "/guru:virama": "\u0A4D", + "/guru:visarga": "\u0A03", + "/guru:ya": "\u0A2F", + "/guru:yakashsign": "\u0A75", + "/guru:za": "\u0A5B", + "/guru:zero": "\u0A66", + "/gyfullwidth": "\u33C9", + "/gysquare": "\u33C9", + "/h": "\u0068", + "/h.inferior": "\u2095", + "/haabkhasiancyrillic": "\u04A9", + "/haabkhcyr": "\u04A9", + "/haaltonearabic": "\u06C1", + "/habengali": "\u09B9", + "/hacirclekatakana": "\u32E9", + "/hacyr": "\u0445", + "/hadescendercyrillic": "\u04B3", + "/hadeva": "\u0939", + "/hafullwidth": "\u33CA", + "/hagujarati": "\u0AB9", + "/hagurmukhi": "\u0A39", + "/hah": "\u062D", + "/hah.fina": "\uFEA2", + "/hah.init": "\uFEA3", + "/hah.init_alefmaksura.fina": "\uFCFF", + "/hah.init_jeem.fina": "\uFC17", + "/hah.init_jeem.medi": "\uFCA9", + "/hah.init_meem.fina": "\uFC18", + "/hah.init_meem.medi": "\uFCAA", + "/hah.init_yeh.fina": "\uFD00", + "/hah.isol": "\uFEA1", + "/hah.medi": "\uFEA4", + "/hah.medi_alefmaksura.fina": "\uFD1B", + "/hah.medi_jeem.medi_yeh.fina": "\uFDBF", + "/hah.medi_meem.medi_alefmaksura.fina": "\uFD5B", + "/hah.medi_meem.medi_yeh.fina": "\uFD5A", + "/hah.medi_yeh.fina": "\uFD1C", + "/hahDigitFourBelow": "\u077C", + "/hahSmallTahAbove": "\u0772", + "/hahSmallTahBelow": "\u076E", + "/hahSmallTahTwoDots": "\u076F", + "/hahThreeDotsUpBelow": "\u0758", + "/hahTwoDotsAbove": "\u0757", + "/haharabic": "\u062D", + "/hahfinalarabic": "\uFEA2", + "/hahhamza": "\u0681", + "/hahinitialarabic": "\uFEA3", + "/hahiragana": "\u306F", + "/hahmedialarabic": "\uFEA4", + "/hahookcyr": "\u04FD", + "/hahthreedotsabove": "\u0685", + "/hahtwodotsvertical": "\u0682", + "/haircut": "\u1F487", + "/hairspace": "\u200A", + "/haitusquare": "\u332A", + "/hakatakana": "\u30CF", + "/hakatakanahalfwidth": "\uFF8A", + "/halantgurmukhi": "\u0A4D", + "/halfcircleleftblack": "\u25D6", + "/halfcirclerightblack": "\u25D7", + "/hamburger": "\u1F354", + "/hammer": "\u1F528", + "/hammerAndWrench": "\u1F6E0", + "/hammerpick": "\u2692", + "/hammersickle": "\u262D", + "/hamsterFace": "\u1F439", + "/hamza": "\u0621", + "/hamzaIsol": "\uFE80", + "/hamzaabove": "\u0654", + "/hamzaarabic": "\u0621", + "/hamzabelow": "\u0655", + "/hamzadammaarabic": "\u0621", + "/hamzadammatanarabic": "\u0621", + "/hamzafathaarabic": "\u0621", + "/hamzafathatanarabic": "\u0621", + "/hamzalowarabic": "\u0621", + "/hamzalowkasraarabic": "\u0621", + "/hamzalowkasratanarabic": "\u0621", + "/hamzasukunarabic": "\u0621", + "/handbag": "\u1F45C", + "/handtailfishhookturned": "\u02AF", + "/hangulchieuchaparen": "\u3217", + "/hangulchieuchparen": "\u3209", + "/hangulcieucaparen": "\u3216", + "/hangulcieucparen": "\u3208", + "/hangulcieucuparen": "\u321C", + "/hanguldottonemarkdbl": "\u302F", + "/hangulfiller": "\u3164", + "/hangulhieuhaparen": "\u321B", + "/hangulhieuhparen": "\u320D", + "/hangulieungaparen": "\u3215", + "/hangulieungparen": "\u3207", + "/hangulkhieukhaparen": "\u3218", + "/hangulkhieukhparen": "\u320A", + "/hangulkiyeokaparen": "\u320E", + "/hangulkiyeokparen": "\u3200", + "/hangulmieumaparen": "\u3212", + "/hangulmieumparen": "\u3204", + "/hangulnieunaparen": "\u320F", + "/hangulnieunparen": "\u3201", + "/hangulphieuphaparen": "\u321A", + "/hangulphieuphparen": "\u320C", + "/hangulpieupaparen": "\u3213", + "/hangulpieupparen": "\u3205", + "/hangulrieulaparen": "\u3211", + "/hangulrieulparen": "\u3203", + "/hangulsingledottonemark": "\u302E", + "/hangulsiosaparen": "\u3214", + "/hangulsiosparen": "\u3206", + "/hangulthieuthaparen": "\u3219", + "/hangulthieuthparen": "\u320B", + "/hangultikeutaparen": "\u3210", + "/hangultikeutparen": "\u3202", + "/happyPersonRaisingOneHand": "\u1F64B", + "/hardDisk": "\u1F5B4", + "/hardcyr": "\u044A", + "/hardsigncyrillic": "\u044A", + "/harpoondownbarbleft": "\u21C3", + "/harpoondownbarbright": "\u21C2", + "/harpoonleftbarbdown": "\u21BD", + "/harpoonleftbarbup": "\u21BC", + "/harpoonrightbarbdown": "\u21C1", + "/harpoonrightbarbup": "\u21C0", + "/harpoonupbarbleft": "\u21BF", + "/harpoonupbarbright": "\u21BE", + "/hasquare": "\u33CA", + "/hastrokecyr": "\u04FF", + "/hatafPatah:hb": "\u05B2", + "/hatafQamats:hb": "\u05B3", + "/hatafSegol:hb": "\u05B1", + "/hatafpatah": "\u05B2", + "/hatafpatah16": "\u05B2", + "/hatafpatah23": "\u05B2", + "/hatafpatah2f": "\u05B2", + "/hatafpatahhebrew": "\u05B2", + "/hatafpatahnarrowhebrew": "\u05B2", + "/hatafpatahquarterhebrew": "\u05B2", + "/hatafpatahwidehebrew": "\u05B2", + "/hatafqamats": "\u05B3", + "/hatafqamats1b": "\u05B3", + "/hatafqamats28": "\u05B3", + "/hatafqamats34": "\u05B3", + "/hatafqamatshebrew": "\u05B3", + "/hatafqamatsnarrowhebrew": "\u05B3", + "/hatafqamatsquarterhebrew": "\u05B3", + "/hatafqamatswidehebrew": "\u05B3", + "/hatafsegol": "\u05B1", + "/hatafsegol17": "\u05B1", + "/hatafsegol24": "\u05B1", + "/hatafsegol30": "\u05B1", + "/hatafsegolhebrew": "\u05B1", + "/hatafsegolnarrowhebrew": "\u05B1", + "/hatafsegolquarterhebrew": "\u05B1", + "/hatafsegolwidehebrew": "\u05B1", + "/hatchingChick": "\u1F423", + "/haveideographiccircled": "\u3292", + "/haveideographicparen": "\u3232", + "/hbar": "\u0127", + "/hbopomofo": "\u310F", + "/hbrevebelow": "\u1E2B", + "/hcaron": "\u021F", + "/hcedilla": "\u1E29", + "/hcircle": "\u24D7", + "/hcircumflex": "\u0125", + "/hcsquare": "\u1F1A6", + "/hdescender": "\u2C68", + "/hdieresis": "\u1E27", + "/hdot": "\u1E23", + "/hdotaccent": "\u1E23", + "/hdotbelow": "\u1E25", + "/hdrsquare": "\u1F1A7", + "/he": "\u05D4", + "/he:hb": "\u05D4", + "/headphone": "\u1F3A7", + "/headstonegraveyard": "\u26FC", + "/hearNoEvilMonkey": "\u1F649", + "/heart": "\u2665", + "/heartArrow": "\u1F498", + "/heartDecoration": "\u1F49F", + "/heartRibbon": "\u1F49D", + "/heartTipOnTheLeft": "\u1F394", + "/heartblack": "\u2665", + "/heartsuitblack": "\u2665", + "/heartsuitwhite": "\u2661", + "/heartwhite": "\u2661", + "/heavyDollarSign": "\u1F4B2", + "/heavyLatinCross": "\u1F547", + "/heavydbldashhorz": "\u254D", + "/heavydbldashvert": "\u254F", + "/heavydn": "\u257B", + "/heavydnhorz": "\u2533", + "/heavydnleft": "\u2513", + "/heavydnright": "\u250F", + "/heavyhorz": "\u2501", + "/heavyleft": "\u2578", + "/heavyleftlightright": "\u257E", + "/heavyquaddashhorz": "\u2509", + "/heavyquaddashvert": "\u250B", + "/heavyright": "\u257A", + "/heavytrpldashhorz": "\u2505", + "/heavytrpldashvert": "\u2507", + "/heavyup": "\u2579", + "/heavyuphorz": "\u253B", + "/heavyupleft": "\u251B", + "/heavyuplightdn": "\u257F", + "/heavyupright": "\u2517", + "/heavyvert": "\u2503", + "/heavyverthorz": "\u254B", + "/heavyvertleft": "\u252B", + "/heavyvertright": "\u2523", + "/hecirclekatakana": "\u32EC", + "/hedagesh": "\uFB34", + "/hedageshhebrew": "\uFB34", + "/hedinterlacedpentagramleft": "\u26E6", + "/hedinterlacedpentagramright": "\u26E5", + "/heh": "\u0647", + "/heh.fina": "\uFEEA", + "/heh.init": "\uFEEB", + "/heh.init_alefmaksura.fina": "\uFC53", + "/heh.init_jeem.fina": "\uFC51", + "/heh.init_jeem.medi": "\uFCD7", + "/heh.init_meem.fina": "\uFC52", + "/heh.init_meem.medi": "\uFCD8", + "/heh.init_meem.medi_jeem.medi": "\uFD93", + "/heh.init_meem.medi_meem.medi": "\uFD94", + "/heh.init_superscriptalef.medi": "\uFCD9", + "/heh.init_yeh.fina": "\uFC54", + "/heh.isol": "\uFEE9", + "/heh.medi": "\uFEEC", + "/hehaltonearabic": "\u06C1", + "/heharabic": "\u0647", + "/hehdoachashmee": "\u06BE", + "/hehdoachashmee.fina": "\uFBAB", + "/hehdoachashmee.init": "\uFBAC", + "/hehdoachashmee.isol": "\uFBAA", + "/hehdoachashmee.medi": "\uFBAD", + "/hehebrew": "\u05D4", + "/hehfinalaltonearabic": "\uFBA7", + "/hehfinalalttwoarabic": "\uFEEA", + "/hehfinalarabic": "\uFEEA", + "/hehgoal": "\u06C1", + "/hehgoal.fina": "\uFBA7", + "/hehgoal.init": "\uFBA8", + "/hehgoal.isol": "\uFBA6", + "/hehgoal.medi": "\uFBA9", + "/hehgoalhamza": "\u06C2", + "/hehhamzaabovefinalarabic": "\uFBA5", + "/hehhamzaaboveisolatedarabic": "\uFBA4", + "/hehinitialaltonearabic": "\uFBA8", + "/hehinitialarabic": "\uFEEB", + "/hehinvertedV": "\u06FF", + "/hehiragana": "\u3078", + "/hehmedialaltonearabic": "\uFBA9", + "/hehmedialarabic": "\uFEEC", + "/hehyeh": "\u06C0", + "/hehyeh.fina": "\uFBA5", + "/hehyeh.isol": "\uFBA4", + "/heiseierasquare": "\u337B", + "/hekatakana": "\u30D8", + "/hekatakanahalfwidth": "\uFF8D", + "/hekutaarusquare": "\u3336", + "/helicopter": "\u1F681", + "/helm": "\u2388", + "/helmetcrosswhite": "\u26D1", + "/heng": "\uA727", + "/henghook": "\u0267", + "/herb": "\u1F33F", + "/hermitianconjugatematrix": "\u22B9", + "/herutusquare": "\u3339", + "/het": "\u05D7", + "/het:hb": "\u05D7", + "/heta": "\u0371", + "/hethebrew": "\u05D7", + "/hewide:hb": "\uFB23", + "/hewithmapiq:hb": "\uFB34", + "/hfishhookturned": "\u02AE", + "/hhalf": "\u2C76", + "/hhook": "\u0266", + "/hhooksuperior": "\u02B1", + "/hhooksupmod": "\u02B1", + "/hi-ressquare": "\u1F1A8", + "/hibiscus": "\u1F33A", + "/hicirclekatakana": "\u32EA", + "/hieuhacirclekorean": "\u327B", + "/hieuhaparenkorean": "\u321B", + "/hieuhcirclekorean": "\u326D", + "/hieuhkorean": "\u314E", + "/hieuhparenkorean": "\u320D", + "/high-heeledShoe": "\u1F460", + "/highBrightness": "\u1F506", + "/highSpeedTrain": "\u1F684", + "/highSpeedTrainWithBulletNose": "\u1F685", + "/highhamza": "\u0674", + "/highideographiccircled": "\u32A4", + "/highvoltage": "\u26A1", + "/hihiragana": "\u3072", + "/hikatakana": "\u30D2", + "/hikatakanahalfwidth": "\uFF8B", + "/hira:a": "\u3042", + "/hira:asmall": "\u3041", + "/hira:ba": "\u3070", + "/hira:be": "\u3079", + "/hira:bi": "\u3073", + "/hira:bo": "\u307C", + "/hira:bu": "\u3076", + "/hira:da": "\u3060", + "/hira:de": "\u3067", + "/hira:di": "\u3062", + "/hira:digraphyori": "\u309F", + "/hira:do": "\u3069", + "/hira:du": "\u3065", + "/hira:e": "\u3048", + "/hira:esmall": "\u3047", + "/hira:ga": "\u304C", + "/hira:ge": "\u3052", + "/hira:gi": "\u304E", + "/hira:go": "\u3054", + "/hira:gu": "\u3050", + "/hira:ha": "\u306F", + "/hira:he": "\u3078", + "/hira:hi": "\u3072", + "/hira:ho": "\u307B", + "/hira:hu": "\u3075", + "/hira:i": "\u3044", + "/hira:ismall": "\u3043", + "/hira:iterationhiragana": "\u309D", + "/hira:ka": "\u304B", + "/hira:kasmall": "\u3095", + "/hira:ke": "\u3051", + "/hira:kesmall": "\u3096", + "/hira:ki": "\u304D", + "/hira:ko": "\u3053", + "/hira:ku": "\u304F", + "/hira:ma": "\u307E", + "/hira:me": "\u3081", + "/hira:mi": "\u307F", + "/hira:mo": "\u3082", + "/hira:mu": "\u3080", + "/hira:n": "\u3093", + "/hira:na": "\u306A", + "/hira:ne": "\u306D", + "/hira:ni": "\u306B", + "/hira:no": "\u306E", + "/hira:nu": "\u306C", + "/hira:o": "\u304A", + "/hira:osmall": "\u3049", + "/hira:pa": "\u3071", + "/hira:pe": "\u307A", + "/hira:pi": "\u3074", + "/hira:po": "\u307D", + "/hira:pu": "\u3077", + "/hira:ra": "\u3089", + "/hira:re": "\u308C", + "/hira:ri": "\u308A", + "/hira:ro": "\u308D", + "/hira:ru": "\u308B", + "/hira:sa": "\u3055", + "/hira:se": "\u305B", + "/hira:semivoicedmarkkana": "\u309C", + "/hira:semivoicedmarkkanacmb": "\u309A", + "/hira:si": "\u3057", + "/hira:so": "\u305D", + "/hira:su": "\u3059", + "/hira:ta": "\u305F", + "/hira:te": "\u3066", + "/hira:ti": "\u3061", + "/hira:to": "\u3068", + "/hira:tu": "\u3064", + "/hira:tusmall": "\u3063", + "/hira:u": "\u3046", + "/hira:usmall": "\u3045", + "/hira:voicediterationhiragana": "\u309E", + "/hira:voicedmarkkana": "\u309B", + "/hira:voicedmarkkanacmb": "\u3099", + "/hira:vu": "\u3094", + "/hira:wa": "\u308F", + "/hira:wasmall": "\u308E", + "/hira:we": "\u3091", + "/hira:wi": "\u3090", + "/hira:wo": "\u3092", + "/hira:ya": "\u3084", + "/hira:yasmall": "\u3083", + "/hira:yo": "\u3088", + "/hira:yosmall": "\u3087", + "/hira:yu": "\u3086", + "/hira:yusmall": "\u3085", + "/hira:za": "\u3056", + "/hira:ze": "\u305C", + "/hira:zi": "\u3058", + "/hira:zo": "\u305E", + "/hira:zu": "\u305A", + "/hiriq": "\u05B4", + "/hiriq14": "\u05B4", + "/hiriq21": "\u05B4", + "/hiriq2d": "\u05B4", + "/hiriq:hb": "\u05B4", + "/hiriqhebrew": "\u05B4", + "/hiriqnarrowhebrew": "\u05B4", + "/hiriqquarterhebrew": "\u05B4", + "/hiriqwidehebrew": "\u05B4", + "/historicsite": "\u26EC", + "/hlinebelow": "\u1E96", + "/hmonospace": "\uFF48", + "/hoarmenian": "\u0570", + "/hocho": "\u1F52A", + "/hocirclekatakana": "\u32ED", + "/hohipthai": "\u0E2B", + "/hohiragana": "\u307B", + "/hokatakana": "\u30DB", + "/hokatakanahalfwidth": "\uFF8E", + "/holam": "\u05B9", + "/holam19": "\u05B9", + "/holam26": "\u05B9", + "/holam32": "\u05B9", + "/holam:hb": "\u05B9", + "/holamHaser:hb": "\u05BA", + "/holamhebrew": "\u05B9", + "/holamnarrowhebrew": "\u05B9", + "/holamquarterhebrew": "\u05B9", + "/holamwidehebrew": "\u05B9", + "/hole": "\u1F573", + "/homotic": "\u223B", + "/honeyPot": "\u1F36F", + "/honeybee": "\u1F41D", + "/honokhukthai": "\u0E2E", + "/honsquare": "\u333F", + "/hook": "\u2440", + "/hookabovecomb": "\u0309", + "/hookcmb": "\u0309", + "/hookpalatalizedbelowcmb": "\u0321", + "/hookretroflexbelowcmb": "\u0322", + "/hoonsquare": "\u3342", + "/hoorusquare": "\u3341", + "/horicoptic": "\u03E9", + "/horizontalTrafficLight": "\u1F6A5", + "/horizontalbar": "\u2015", + "/horizontalbarwhitearrowonpedestalup": "\u21EC", + "/horizontalmalestroke": "\u26A9", + "/horncmb": "\u031B", + "/horse": "\u1F40E", + "/horseFace": "\u1F434", + "/horseRacing": "\u1F3C7", + "/hospital": "\u1F3E5", + "/hotDog": "\u1F32D", + "/hotPepper": "\u1F336", + "/hotbeverage": "\u2615", + "/hotel": "\u1F3E8", + "/hotsprings": "\u2668", + "/hourglass": "\u231B", + "/hourglassflowings": "\u23F3", + "/house": "\u2302", + "/houseBuilding": "\u1F3E0", + "/houseBuildings": "\u1F3D8", + "/houseGarden": "\u1F3E1", + "/hpafullwidth": "\u3371", + "/hpalatalhook": "\uA795", + "/hparen": "\u24A3", + "/hparenthesized": "\u24A3", + "/hpfullwidth": "\u33CB", + "/hryvnia": "\u20B4", + "/hsuperior": "\u02B0", + "/hsupmod": "\u02B0", + "/hturned": "\u0265", + "/htypeopencircuit": "\u238F", + "/huaraddosquare": "\u3332", + "/hucirclekatakana": "\u32EB", + "/huhiragana": "\u3075", + "/huiitosquare": "\u3333", + "/hukatakana": "\u30D5", + "/hukatakanahalfwidth": "\uFF8C", + "/hundredPoints": "\u1F4AF", + "/hundredthousandscmbcyr": "\u0488", + "/hungarumlaut": "\u02DD", + "/hungarumlautcmb": "\u030B", + "/huransquare": "\u3335", + "/hushedFace": "\u1F62F", + "/hv": "\u0195", + "/hwd:a": "\uFFC2", + "/hwd:ae": "\uFFC3", + "/hwd:blacksquare": "\uFFED", + "/hwd:chieuch": "\uFFBA", + "/hwd:cieuc": "\uFFB8", + "/hwd:downwardsarrow": "\uFFEC", + "/hwd:e": "\uFFC7", + "/hwd:eo": "\uFFC6", + "/hwd:eu": "\uFFDA", + "/hwd:formslightvertical": "\uFFE8", + "/hwd:hangulfiller": "\uFFA0", + "/hwd:hieuh": "\uFFBE", + "/hwd:i": "\uFFDC", + "/hwd:ideographiccomma": "\uFF64", + "/hwd:ideographicfullstop": "\uFF61", + "/hwd:ieung": "\uFFB7", + "/hwd:kata:a": "\uFF71", + "/hwd:kata:asmall": "\uFF67", + "/hwd:kata:e": "\uFF74", + "/hwd:kata:esmall": "\uFF6A", + "/hwd:kata:ha": "\uFF8A", + "/hwd:kata:he": "\uFF8D", + "/hwd:kata:hi": "\uFF8B", + "/hwd:kata:ho": "\uFF8E", + "/hwd:kata:hu": "\uFF8C", + "/hwd:kata:i": "\uFF72", + "/hwd:kata:ismall": "\uFF68", + "/hwd:kata:ka": "\uFF76", + "/hwd:kata:ke": "\uFF79", + "/hwd:kata:ki": "\uFF77", + "/hwd:kata:ko": "\uFF7A", + "/hwd:kata:ku": "\uFF78", + "/hwd:kata:ma": "\uFF8F", + "/hwd:kata:me": "\uFF92", + "/hwd:kata:mi": "\uFF90", + "/hwd:kata:middledot": "\uFF65", + "/hwd:kata:mo": "\uFF93", + "/hwd:kata:mu": "\uFF91", + "/hwd:kata:n": "\uFF9D", + "/hwd:kata:na": "\uFF85", + "/hwd:kata:ne": "\uFF88", + "/hwd:kata:ni": "\uFF86", + "/hwd:kata:no": "\uFF89", + "/hwd:kata:nu": "\uFF87", + "/hwd:kata:o": "\uFF75", + "/hwd:kata:osmall": "\uFF6B", + "/hwd:kata:prolongedkana": "\uFF70", + "/hwd:kata:ra": "\uFF97", + "/hwd:kata:re": "\uFF9A", + "/hwd:kata:ri": "\uFF98", + "/hwd:kata:ro": "\uFF9B", + "/hwd:kata:ru": "\uFF99", + "/hwd:kata:sa": "\uFF7B", + "/hwd:kata:se": "\uFF7E", + "/hwd:kata:semi-voiced": "\uFF9F", + "/hwd:kata:si": "\uFF7C", + "/hwd:kata:so": "\uFF7F", + "/hwd:kata:su": "\uFF7D", + "/hwd:kata:ta": "\uFF80", + "/hwd:kata:te": "\uFF83", + "/hwd:kata:ti": "\uFF81", + "/hwd:kata:to": "\uFF84", + "/hwd:kata:tu": "\uFF82", + "/hwd:kata:tusmall": "\uFF6F", + "/hwd:kata:u": "\uFF73", + "/hwd:kata:usmall": "\uFF69", + "/hwd:kata:voiced": "\uFF9E", + "/hwd:kata:wa": "\uFF9C", + "/hwd:kata:wo": "\uFF66", + "/hwd:kata:ya": "\uFF94", + "/hwd:kata:yasmall": "\uFF6C", + "/hwd:kata:yo": "\uFF96", + "/hwd:kata:yosmall": "\uFF6E", + "/hwd:kata:yu": "\uFF95", + "/hwd:kata:yusmall": "\uFF6D", + "/hwd:khieukh": "\uFFBB", + "/hwd:kiyeok": "\uFFA1", + "/hwd:kiyeoksios": "\uFFA3", + "/hwd:leftcornerbracket": "\uFF62", + "/hwd:leftwardsarrow": "\uFFE9", + "/hwd:mieum": "\uFFB1", + "/hwd:nieun": "\uFFA4", + "/hwd:nieuncieuc": "\uFFA5", + "/hwd:nieunhieuh": "\uFFA6", + "/hwd:o": "\uFFCC", + "/hwd:oe": "\uFFCF", + "/hwd:phieuph": "\uFFBD", + "/hwd:pieup": "\uFFB2", + "/hwd:pieupsios": "\uFFB4", + "/hwd:rieul": "\uFFA9", + "/hwd:rieulhieuh": "\uFFB0", + "/hwd:rieulkiyeok": "\uFFAA", + "/hwd:rieulmieum": "\uFFAB", + "/hwd:rieulphieuph": "\uFFAF", + "/hwd:rieulpieup": "\uFFAC", + "/hwd:rieulsios": "\uFFAD", + "/hwd:rieulthieuth": "\uFFAE", + "/hwd:rightcornerbracket": "\uFF63", + "/hwd:rightwardsarrow": "\uFFEB", + "/hwd:sios": "\uFFB5", + "/hwd:ssangcieuc": "\uFFB9", + "/hwd:ssangkiyeok": "\uFFA2", + "/hwd:ssangpieup": "\uFFB3", + "/hwd:ssangsios": "\uFFB6", + "/hwd:ssangtikeut": "\uFFA8", + "/hwd:thieuth": "\uFFBC", + "/hwd:tikeut": "\uFFA7", + "/hwd:u": "\uFFD3", + "/hwd:upwardsarrow": "\uFFEA", + "/hwd:wa": "\uFFCD", + "/hwd:wae": "\uFFCE", + "/hwd:we": "\uFFD5", + "/hwd:weo": "\uFFD4", + "/hwd:whitecircle": "\uFFEE", + "/hwd:wi": "\uFFD6", + "/hwd:ya": "\uFFC4", + "/hwd:yae": "\uFFC5", + "/hwd:ye": "\uFFCB", + "/hwd:yeo": "\uFFCA", + "/hwd:yi": "\uFFDB", + "/hwd:yo": "\uFFD2", + "/hwd:yu": "\uFFD7", + "/hyphen": "\u002D", + "/hyphenationpoint": "\u2027", + "/hyphenbullet": "\u2043", + "/hyphendbl": "\u2E40", + "/hyphendbloblique": "\u2E17", + "/hyphendieresis": "\u2E1A", + "/hypheninferior": "\uF6E5", + "/hyphenminus": "\u002D", + "/hyphenmonospace": "\uFF0D", + "/hyphensmall": "\uFE63", + "/hyphensoft": "\u00AD", + "/hyphensuperior": "\uF6E6", + "/hyphentwo": "\u2010", + "/hypodiastole": "\u2E12", + "/hysteresis": "\u238E", + "/hzfullwidth": "\u3390", + "/i": "\u0069", + "/i.superior": "\u2071", + "/iacute": "\u00ED", + "/iacyrillic": "\u044F", + "/iaepigraphic": "\uA7FE", + "/ibengali": "\u0987", + "/ibopomofo": "\u3127", + "/ibreve": "\u012D", + "/icaron": "\u01D0", + "/iceCream": "\u1F368", + "/iceHockeyStickAndPuck": "\u1F3D2", + "/iceskate": "\u26F8", + "/icircle": "\u24D8", + "/icirclekatakana": "\u32D1", + "/icircumflex": "\u00EE", + "/icyr": "\u0438", + "/icyrillic": "\u0456", + "/idblgrave": "\u0209", + "/idblstruckitalic": "\u2148", + "/ideographearthcircle": "\u328F", + "/ideographfirecircle": "\u328B", + "/ideographicallianceparen": "\u323F", + "/ideographiccallparen": "\u323A", + "/ideographiccentrecircle": "\u32A5", + "/ideographicclose": "\u3006", + "/ideographiccomma": "\u3001", + "/ideographiccommaleft": "\uFF64", + "/ideographiccongratulationparen": "\u3237", + "/ideographiccorrectcircle": "\u32A3", + "/ideographicdepartingtonemark": "\u302C", + "/ideographicearthparen": "\u322F", + "/ideographicenteringtonemark": "\u302D", + "/ideographicenterpriseparen": "\u323D", + "/ideographicexcellentcircle": "\u329D", + "/ideographicfestivalparen": "\u3240", + "/ideographicfinancialcircle": "\u3296", + "/ideographicfinancialparen": "\u3236", + "/ideographicfireparen": "\u322B", + "/ideographichalffillspace": "\u303F", + "/ideographichaveparen": "\u3232", + "/ideographichighcircle": "\u32A4", + "/ideographiciterationmark": "\u3005", + "/ideographiclaborcircle": "\u3298", + "/ideographiclaborparen": "\u3238", + "/ideographicleftcircle": "\u32A7", + "/ideographicleveltonemark": "\u302A", + "/ideographiclowcircle": "\u32A6", + "/ideographicmedicinecircle": "\u32A9", + "/ideographicmetalparen": "\u322E", + "/ideographicmoonparen": "\u322A", + "/ideographicnameparen": "\u3234", + "/ideographicperiod": "\u3002", + "/ideographicprintcircle": "\u329E", + "/ideographicreachparen": "\u3243", + "/ideographicrepresentparen": "\u3239", + "/ideographicresourceparen": "\u323E", + "/ideographicrightcircle": "\u32A8", + "/ideographicrisingtonemark": "\u302B", + "/ideographicsecretcircle": "\u3299", + "/ideographicselfparen": "\u3242", + "/ideographicsocietyparen": "\u3233", + "/ideographicspace": "\u3000", + "/ideographicspecialparen": "\u3235", + "/ideographicstockparen": "\u3231", + "/ideographicstudyparen": "\u323B", + "/ideographicsunparen": "\u3230", + "/ideographicsuperviseparen": "\u323C", + "/ideographictelegraphlinefeedseparatorsymbol": "\u3037", + "/ideographictelegraphsymbolforhoureight": "\u3360", + "/ideographictelegraphsymbolforhoureighteen": "\u336A", + "/ideographictelegraphsymbolforhoureleven": "\u3363", + "/ideographictelegraphsymbolforhourfifteen": "\u3367", + "/ideographictelegraphsymbolforhourfive": "\u335D", + "/ideographictelegraphsymbolforhourfour": "\u335C", + "/ideographictelegraphsymbolforhourfourteen": "\u3366", + "/ideographictelegraphsymbolforhournine": "\u3361", + "/ideographictelegraphsymbolforhournineteen": "\u336B", + "/ideographictelegraphsymbolforhourone": "\u3359", + "/ideographictelegraphsymbolforhourseven": "\u335F", + "/ideographictelegraphsymbolforhourseventeen": "\u3369", + "/ideographictelegraphsymbolforhoursix": "\u335E", + "/ideographictelegraphsymbolforhoursixteen": "\u3368", + "/ideographictelegraphsymbolforhourten": "\u3362", + "/ideographictelegraphsymbolforhourthirteen": "\u3365", + "/ideographictelegraphsymbolforhourthree": "\u335B", + "/ideographictelegraphsymbolforhourtwelve": "\u3364", + "/ideographictelegraphsymbolforhourtwenty": "\u336C", + "/ideographictelegraphsymbolforhourtwentyfour": "\u3370", + "/ideographictelegraphsymbolforhourtwentyone": "\u336D", + "/ideographictelegraphsymbolforhourtwentythree": "\u336F", + "/ideographictelegraphsymbolforhourtwentytwo": "\u336E", + "/ideographictelegraphsymbolforhourtwo": "\u335A", + "/ideographictelegraphsymbolforhourzero": "\u3358", + "/ideographicvariationindicator": "\u303E", + "/ideographicwaterparen": "\u322C", + "/ideographicwoodparen": "\u322D", + "/ideographiczero": "\u3007", + "/ideographmetalcircle": "\u328E", + "/ideographmooncircle": "\u328A", + "/ideographnamecircle": "\u3294", + "/ideographsuncircle": "\u3290", + "/ideographwatercircle": "\u328C", + "/ideographwoodcircle": "\u328D", + "/ideva": "\u0907", + "/idieresis": "\u00EF", + "/idieresisacute": "\u1E2F", + "/idieresiscyr": "\u04E5", + "/idieresiscyrillic": "\u04E5", + "/idotbelow": "\u1ECB", + "/idsquare": "\u1F194", + "/iebrevecyr": "\u04D7", + "/iebrevecyrillic": "\u04D7", + "/iecyr": "\u0435", + "/iecyrillic": "\u0435", + "/iegravecyr": "\u0450", + "/iepigraphicsideways": "\uA7F7", + "/ieungacirclekorean": "\u3275", + "/ieungaparenkorean": "\u3215", + "/ieungcirclekorean": "\u3267", + "/ieungkorean": "\u3147", + "/ieungparenkorean": "\u3207", + "/ieungucirclekorean": "\u327E", + "/igrave": "\u00EC", + "/igravecyr": "\u045D", + "/igravedbl": "\u0209", + "/igujarati": "\u0A87", + "/igurmukhi": "\u0A07", + "/ihiragana": "\u3044", + "/ihoi": "\u1EC9", + "/ihookabove": "\u1EC9", + "/iibengali": "\u0988", + "/iicyrillic": "\u0438", + "/iideva": "\u0908", + "/iigujarati": "\u0A88", + "/iigurmukhi": "\u0A08", + "/iimatragurmukhi": "\u0A40", + "/iinvertedbreve": "\u020B", + "/iishortcyrillic": "\u0439", + "/iivowelsignbengali": "\u09C0", + "/iivowelsigndeva": "\u0940", + "/iivowelsigngujarati": "\u0AC0", + "/ij": "\u0133", + "/ikatakana": "\u30A4", + "/ikatakanahalfwidth": "\uFF72", + "/ikawi": "\uA985", + "/ikorean": "\u3163", + "/ilde": "\u02DC", + "/iluy:hb": "\u05AC", + "/iluyhebrew": "\u05AC", + "/imacron": "\u012B", + "/imacroncyr": "\u04E3", + "/imacroncyrillic": "\u04E3", + "/image": "\u22B7", + "/imageorapproximatelyequal": "\u2253", + "/imatragurmukhi": "\u0A3F", + "/imonospace": "\uFF49", + "/imp": "\u1F47F", + "/inboxTray": "\u1F4E5", + "/incomingEnvelope": "\u1F4E8", + "/increaseFontSize": "\u1F5DA", + "/increment": "\u2206", + "/indianrupee": "\u20B9", + "/infinity": "\u221E", + "/information": "\u2139", + "/infullwidth": "\u33CC", + "/inhibitarabicformshaping": "\u206C", + "/inhibitsymmetricswapping": "\u206A", + "/iniarmenian": "\u056B", + "/iningusquare": "\u3304", + "/inmationDeskPerson": "\u1F481", + "/inputLatinCapitalLetters": "\u1F520", + "/inputLatinLetters": "\u1F524", + "/inputLatinSmallLetters": "\u1F521", + "/inputNumbers": "\u1F522", + "/inputS": "\u1F523", + "/insertion": "\u2380", + "/integral": "\u222B", + "/integralbottom": "\u2321", + "/integralbt": "\u2321", + "/integralclockwise": "\u2231", + "/integralcontour": "\u222E", + "/integralcontouranticlockwise": "\u2233", + "/integralcontourclockwise": "\u2232", + "/integraldbl": "\u222C", + "/integralex": "\uF8F5", + "/integralextension": "\u23AE", + "/integralsurface": "\u222F", + "/integraltop": "\u2320", + "/integraltp": "\u2320", + "/integraltpl": "\u222D", + "/integralvolume": "\u2230", + "/intercalate": "\u22BA", + "/interlinearanchor": "\uFFF9", + "/interlinearseparator": "\uFFFA", + "/interlinearterminator": "\uFFFB", + "/interlockedfemalemale": "\u26A4", + "/interrobang": "\u203D", + "/interrobanginverted": "\u2E18", + "/intersection": "\u2229", + "/intersectionarray": "\u22C2", + "/intersectiondbl": "\u22D2", + "/intisquare": "\u3305", + "/invbullet": "\u25D8", + "/invcircle": "\u25D9", + "/inverteddamma": "\u0657", + "/invertedfork": "\u2443", + "/invertedpentagram": "\u26E7", + "/invertedundertie": "\u2054", + "/invisibleplus": "\u2064", + "/invisibleseparator": "\u2063", + "/invisibletimes": "\u2062", + "/invsmileface": "\u263B", + "/iocyr": "\u0451", + "/iocyrillic": "\u0451", + "/iogonek": "\u012F", + "/iota": "\u03B9", + "/iotaacute": "\u1F77", + "/iotaadscript": "\u1FBE", + "/iotaasper": "\u1F31", + "/iotaasperacute": "\u1F35", + "/iotaaspergrave": "\u1F33", + "/iotaaspertilde": "\u1F37", + "/iotabreve": "\u1FD0", + "/iotadieresis": "\u03CA", + "/iotadieresisacute": "\u1FD3", + "/iotadieresisgrave": "\u1FD2", + "/iotadieresistilde": "\u1FD7", + "/iotadieresistonos": "\u0390", + "/iotafunc": "\u2373", + "/iotagrave": "\u1F76", + "/iotalatin": "\u0269", + "/iotalenis": "\u1F30", + "/iotalenisacute": "\u1F34", + "/iotalenisgrave": "\u1F32", + "/iotalenistilde": "\u1F36", + "/iotasub": "\u037A", + "/iotatilde": "\u1FD6", + "/iotatonos": "\u03AF", + "/iotaturned": "\u2129", + "/iotaunderlinefunc": "\u2378", + "/iotawithmacron": "\u1FD1", + "/ipa:Ismall": "\u026A", + "/ipa:alpha": "\u0251", + "/ipa:ereversed": "\u0258", + "/ipa:esh": "\u0283", + "/ipa:gamma": "\u0263", + "/ipa:glottalstop": "\u0294", + "/ipa:gscript": "\u0261", + "/ipa:iota": "\u0269", + "/ipa:phi": "\u0278", + "/ipa:rtail": "\u027D", + "/ipa:schwa": "\u0259", + "/ipa:upsilon": "\u028A", + "/iparen": "\u24A4", + "/iparenthesized": "\u24A4", + "/irigurmukhi": "\u0A72", + "/is": "\uA76D", + "/isen-isenpada": "\uA9DF", + "/ishortcyr": "\u0439", + "/ishortsharptailcyr": "\u048B", + "/ismallhiragana": "\u3043", + "/ismallkatakana": "\u30A3", + "/ismallkatakanahalfwidth": "\uFF68", + "/issharbengali": "\u09FA", + "/istroke": "\u0268", + "/isuperior": "\uF6ED", + "/itemideographiccircled": "\u32A0", + "/iterationhiragana": "\u309D", + "/iterationkatakana": "\u30FD", + "/itilde": "\u0129", + "/itildebelow": "\u1E2D", + "/iubopomofo": "\u3129", + "/iucyrillic": "\u044E", + "/iufullwidth": "\u337A", + "/iukrcyr": "\u0456", + "/ivowelsignbengali": "\u09BF", + "/ivowelsigndeva": "\u093F", + "/ivowelsigngujarati": "\u0ABF", + "/izakayaLantern": "\u1F3EE", + "/izhitsacyr": "\u0475", + "/izhitsacyrillic": "\u0475", + "/izhitsadblgravecyrillic": "\u0477", + "/izhitsagravedblcyr": "\u0477", + "/j": "\u006A", + "/j.inferior": "\u2C7C", + "/jaarmenian": "\u0571", + "/jabengali": "\u099C", + "/jackOLantern": "\u1F383", + "/jadeva": "\u091C", + "/jagujarati": "\u0A9C", + "/jagurmukhi": "\u0A1C", + "/jamahaprana": "\uA999", + "/januarytelegraph": "\u32C0", + "/japaneseBeginner": "\u1F530", + "/japaneseCastle": "\u1F3EF", + "/japaneseDolls": "\u1F38E", + "/japaneseGoblin": "\u1F47A", + "/japaneseOgre": "\u1F479", + "/japanesePostOffice": "\u1F3E3", + "/japanesebank": "\u26FB", + "/java:a": "\uA984", + "/java:ai": "\uA98D", + "/java:ba": "\uA9A7", + "/java:ca": "\uA995", + "/java:da": "\uA9A2", + "/java:dda": "\uA99D", + "/java:e": "\uA98C", + "/java:eight": "\uA9D8", + "/java:five": "\uA9D5", + "/java:four": "\uA9D4", + "/java:ga": "\uA992", + "/java:ha": "\uA9B2", + "/java:i": "\uA986", + "/java:ii": "\uA987", + "/java:ja": "\uA997", + "/java:ka": "\uA98F", + "/java:la": "\uA9AD", + "/java:ma": "\uA9A9", + "/java:na": "\uA9A4", + "/java:nga": "\uA994", + "/java:nine": "\uA9D9", + "/java:nya": "\uA99A", + "/java:o": "\uA98E", + "/java:one": "\uA9D1", + "/java:pa": "\uA9A5", + "/java:ra": "\uA9AB", + "/java:sa": "\uA9B1", + "/java:seven": "\uA9D7", + "/java:six": "\uA9D6", + "/java:ta": "\uA9A0", + "/java:three": "\uA9D3", + "/java:tta": "\uA99B", + "/java:two": "\uA9D2", + "/java:u": "\uA988", + "/java:wa": "\uA9AE", + "/java:ya": "\uA9AA", + "/java:zero": "\uA9D0", + "/jbopomofo": "\u3110", + "/jcaron": "\u01F0", + "/jcircle": "\u24D9", + "/jcircumflex": "\u0135", + "/jcrossedtail": "\u029D", + "/jdblstruckitalic": "\u2149", + "/jdotlessstroke": "\u025F", + "/jeans": "\u1F456", + "/jecyr": "\u0458", + "/jecyrillic": "\u0458", + "/jeem": "\u062C", + "/jeem.fina": "\uFE9E", + "/jeem.init": "\uFE9F", + "/jeem.init_alefmaksura.fina": "\uFD01", + "/jeem.init_hah.fina": "\uFC15", + "/jeem.init_hah.medi": "\uFCA7", + "/jeem.init_meem.fina": "\uFC16", + "/jeem.init_meem.medi": "\uFCA8", + "/jeem.init_meem.medi_hah.medi": "\uFD59", + "/jeem.init_yeh.fina": "\uFD02", + "/jeem.isol": "\uFE9D", + "/jeem.medi": "\uFEA0", + "/jeem.medi_alefmaksura.fina": "\uFD1D", + "/jeem.medi_hah.medi_alefmaksura.fina": "\uFDA6", + "/jeem.medi_hah.medi_yeh.fina": "\uFDBE", + "/jeem.medi_meem.medi_alefmaksura.fina": "\uFDA7", + "/jeem.medi_meem.medi_hah.fina": "\uFD58", + "/jeem.medi_meem.medi_yeh.fina": "\uFDA5", + "/jeem.medi_yeh.fina": "\uFD1E", + "/jeemabove": "\u06DA", + "/jeemarabic": "\u062C", + "/jeemfinalarabic": "\uFE9E", + "/jeeminitialarabic": "\uFE9F", + "/jeemmedialarabic": "\uFEA0", + "/jeh": "\u0698", + "/jeh.fina": "\uFB8B", + "/jeh.isol": "\uFB8A", + "/jeharabic": "\u0698", + "/jehfinalarabic": "\uFB8B", + "/jhabengali": "\u099D", + "/jhadeva": "\u091D", + "/jhagujarati": "\u0A9D", + "/jhagurmukhi": "\u0A1D", + "/jheharmenian": "\u057B", + "/jis": "\u3004", + "/jiterup": "\u2643", + "/jmonospace": "\uFF4A", + "/jotdiaeresisfunc": "\u2364", + "/jotunderlinefunc": "\u235B", + "/joystick": "\u1F579", + "/jparen": "\u24A5", + "/jparenthesized": "\u24A5", + "/jstroke": "\u0249", + "/jsuperior": "\u02B2", + "/jsupmod": "\u02B2", + "/jueuicircle": "\u327D", + "/julytelegraph": "\u32C6", + "/junetelegraph": "\u32C5", + "/juno": "\u26B5", + "/k": "\u006B", + "/k.inferior": "\u2096", + "/kaaba": "\u1F54B", + "/kaaleutcyr": "\u051F", + "/kabashkcyr": "\u04A1", + "/kabashkircyrillic": "\u04A1", + "/kabengali": "\u0995", + "/kacirclekatakana": "\u32D5", + "/kacute": "\u1E31", + "/kacyr": "\u043A", + "/kacyrillic": "\u043A", + "/kadescendercyrillic": "\u049B", + "/kadeva": "\u0915", + "/kaf": "\u05DB", + "/kaf.fina": "\uFEDA", + "/kaf.init": "\uFEDB", + "/kaf.init_alef.fina": "\uFC37", + "/kaf.init_alefmaksura.fina": "\uFC3D", + "/kaf.init_hah.fina": "\uFC39", + "/kaf.init_hah.medi": "\uFCC5", + "/kaf.init_jeem.fina": "\uFC38", + "/kaf.init_jeem.medi": "\uFCC4", + "/kaf.init_khah.fina": "\uFC3A", + "/kaf.init_khah.medi": "\uFCC6", + "/kaf.init_lam.fina": "\uFC3B", + "/kaf.init_lam.medi": "\uFCC7", + "/kaf.init_meem.fina": "\uFC3C", + "/kaf.init_meem.medi": "\uFCC8", + "/kaf.init_meem.medi_meem.medi": "\uFDC3", + "/kaf.init_yeh.fina": "\uFC3E", + "/kaf.isol": "\uFED9", + "/kaf.medi": "\uFEDC", + "/kaf.medi_alef.fina": "\uFC80", + "/kaf.medi_alefmaksura.fina": "\uFC83", + "/kaf.medi_lam.fina": "\uFC81", + "/kaf.medi_lam.medi": "\uFCEB", + "/kaf.medi_meem.fina": "\uFC82", + "/kaf.medi_meem.medi": "\uFCEC", + "/kaf.medi_meem.medi_meem.fina": "\uFDBB", + "/kaf.medi_meem.medi_yeh.fina": "\uFDB7", + "/kaf.medi_yeh.fina": "\uFC84", + "/kaf:hb": "\u05DB", + "/kafTwoDotsAbove": "\u077F", + "/kafarabic": "\u0643", + "/kafdagesh": "\uFB3B", + "/kafdageshhebrew": "\uFB3B", + "/kafdotabove": "\u06AC", + "/kaffinalarabic": "\uFEDA", + "/kafhebrew": "\u05DB", + "/kafinitialarabic": "\uFEDB", + "/kafmedialarabic": "\uFEDC", + "/kafrafehebrew": "\uFB4D", + "/kafring": "\u06AB", + "/kafswash": "\u06AA", + "/kafthreedotsbelow": "\u06AE", + "/kafullwidth": "\u3384", + "/kafwide:hb": "\uFB24", + "/kafwithdagesh:hb": "\uFB3B", + "/kafwithrafe:hb": "\uFB4D", + "/kagujarati": "\u0A95", + "/kagurmukhi": "\u0A15", + "/kahiragana": "\u304B", + "/kahookcyr": "\u04C4", + "/kahookcyrillic": "\u04C4", + "/kairisquare": "\u330B", + "/kaisymbol": "\u03D7", + "/kakatakana": "\u30AB", + "/kakatakanahalfwidth": "\uFF76", + "/kamurda": "\uA991", + "/kappa": "\u03BA", + "/kappa.math": "\u03F0", + "/kappasymbolgreek": "\u03F0", + "/kapyeounmieumkorean": "\u3171", + "/kapyeounphieuphkorean": "\u3184", + "/kapyeounpieupkorean": "\u3178", + "/kapyeounssangpieupkorean": "\u3179", + "/karattosquare": "\u330C", + "/karoriisquare": "\u330D", + "/kasasak": "\uA990", + "/kashida": "\u0640", + "/kashidaFina": "\uFE73", + "/kashidaautoarabic": "\u0640", + "/kashidaautonosidebearingarabic": "\u0640", + "/kashmiriyeh": "\u0620", + "/kasmallkatakana": "\u30F5", + "/kasquare": "\u3384", + "/kasra": "\u0650", + "/kasraIsol": "\uFE7A", + "/kasraMedi": "\uFE7B", + "/kasraarabic": "\u0650", + "/kasrasmall": "\u061A", + "/kasratan": "\u064D", + "/kasratanIsol": "\uFE74", + "/kasratanarabic": "\u064D", + "/kastrokecyr": "\u049F", + "/kastrokecyrillic": "\u049F", + "/kata:a": "\u30A2", + "/kata:asmall": "\u30A1", + "/kata:ba": "\u30D0", + "/kata:be": "\u30D9", + "/kata:bi": "\u30D3", + "/kata:bo": "\u30DC", + "/kata:bu": "\u30D6", + "/kata:da": "\u30C0", + "/kata:de": "\u30C7", + "/kata:di": "\u30C2", + "/kata:digraphkoto": "\u30FF", + "/kata:do": "\u30C9", + "/kata:doublehyphenkana": "\u30A0", + "/kata:du": "\u30C5", + "/kata:e": "\u30A8", + "/kata:esmall": "\u30A7", + "/kata:ga": "\u30AC", + "/kata:ge": "\u30B2", + "/kata:gi": "\u30AE", + "/kata:go": "\u30B4", + "/kata:gu": "\u30B0", + "/kata:ha": "\u30CF", + "/kata:he": "\u30D8", + "/kata:hi": "\u30D2", + "/kata:ho": "\u30DB", + "/kata:hu": "\u30D5", + "/kata:i": "\u30A4", + "/kata:ismall": "\u30A3", + "/kata:iteration": "\u30FD", + "/kata:ka": "\u30AB", + "/kata:kasmall": "\u30F5", + "/kata:ke": "\u30B1", + "/kata:kesmall": "\u30F6", + "/kata:ki": "\u30AD", + "/kata:ko": "\u30B3", + "/kata:ku": "\u30AF", + "/kata:ma": "\u30DE", + "/kata:me": "\u30E1", + "/kata:mi": "\u30DF", + "/kata:middledot": "\u30FB", + "/kata:mo": "\u30E2", + "/kata:mu": "\u30E0", + "/kata:n": "\u30F3", + "/kata:na": "\u30CA", + "/kata:ne": "\u30CD", + "/kata:ni": "\u30CB", + "/kata:no": "\u30CE", + "/kata:nu": "\u30CC", + "/kata:o": "\u30AA", + "/kata:osmall": "\u30A9", + "/kata:pa": "\u30D1", + "/kata:pe": "\u30DA", + "/kata:pi": "\u30D4", + "/kata:po": "\u30DD", + "/kata:prolongedkana": "\u30FC", + "/kata:pu": "\u30D7", + "/kata:ra": "\u30E9", + "/kata:re": "\u30EC", + "/kata:ri": "\u30EA", + "/kata:ro": "\u30ED", + "/kata:ru": "\u30EB", + "/kata:sa": "\u30B5", + "/kata:se": "\u30BB", + "/kata:si": "\u30B7", + "/kata:so": "\u30BD", + "/kata:su": "\u30B9", + "/kata:ta": "\u30BF", + "/kata:te": "\u30C6", + "/kata:ti": "\u30C1", + "/kata:to": "\u30C8", + "/kata:tu": "\u30C4", + "/kata:tusmall": "\u30C3", + "/kata:u": "\u30A6", + "/kata:usmall": "\u30A5", + "/kata:va": "\u30F7", + "/kata:ve": "\u30F9", + "/kata:vi": "\u30F8", + "/kata:vo": "\u30FA", + "/kata:voicediteration": "\u30FE", + "/kata:vu": "\u30F4", + "/kata:wa": "\u30EF", + "/kata:wasmall": "\u30EE", + "/kata:we": "\u30F1", + "/kata:wi": "\u30F0", + "/kata:wo": "\u30F2", + "/kata:ya": "\u30E4", + "/kata:yasmall": "\u30E3", + "/kata:yo": "\u30E8", + "/kata:yosmall": "\u30E7", + "/kata:yu": "\u30E6", + "/kata:yusmall": "\u30E5", + "/kata:za": "\u30B6", + "/kata:ze": "\u30BC", + "/kata:zi": "\u30B8", + "/kata:zo": "\u30BE", + "/kata:zu": "\u30BA", + "/katahiraprolongmarkhalfwidth": "\uFF70", + "/katailcyr": "\u049B", + "/kaverticalstrokecyr": "\u049D", + "/kaverticalstrokecyrillic": "\u049D", + "/kavykainvertedlow": "\u2E45", + "/kavykalow": "\u2E47", + "/kavykawithdotlow": "\u2E48", + "/kavykawithkavykaaboveinvertedlow": "\u2E46", + "/kbfullwidth": "\u3385", + "/kbopomofo": "\u310E", + "/kcalfullwidth": "\u3389", + "/kcalsquare": "\u3389", + "/kcaron": "\u01E9", + "/kcedilla": "\u0137", + "/kcircle": "\u24DA", + "/kcommaaccent": "\u0137", + "/kdescender": "\u2C6A", + "/kdiagonalstroke": "\uA743", + "/kdotbelow": "\u1E33", + "/kecirclekatakana": "\u32D8", + "/keesusquare": "\u331C", + "/keharmenian": "\u0584", + "/keheh": "\u06A9", + "/keheh.fina": "\uFB8F", + "/keheh.init": "\uFB90", + "/keheh.isol": "\uFB8E", + "/keheh.medi": "\uFB91", + "/kehehDotAbove": "\u0762", + "/kehehThreeDotsAbove": "\u0763", + "/kehehThreeDotsUpBelow": "\u0764", + "/kehehthreedotsbelow": "\u063C", + "/kehehtwodotsabove": "\u063B", + "/kehiragana": "\u3051", + "/kekatakana": "\u30B1", + "/kekatakanahalfwidth": "\uFF79", + "/kelvin": "\u212A", + "/kenarmenian": "\u056F", + "/keretconsonant": "\uA9BD", + "/kesmallkatakana": "\u30F6", + "/key": "\u1F511", + "/keyboardAndMouse": "\u1F5A6", + "/keycapTen": "\u1F51F", + "/kgfullwidth": "\u338F", + "/kgreenlandic": "\u0138", + "/khabengali": "\u0996", + "/khacyrillic": "\u0445", + "/khadeva": "\u0916", + "/khagujarati": "\u0A96", + "/khagurmukhi": "\u0A16", + "/khah": "\u062E", + "/khah.fina": "\uFEA6", + "/khah.init": "\uFEA7", + "/khah.init_alefmaksura.fina": "\uFD03", + "/khah.init_hah.fina": "\uFC1A", + "/khah.init_jeem.fina": "\uFC19", + "/khah.init_jeem.medi": "\uFCAB", + "/khah.init_meem.fina": "\uFC1B", + "/khah.init_meem.medi": "\uFCAC", + "/khah.init_yeh.fina": "\uFD04", + "/khah.isol": "\uFEA5", + "/khah.medi": "\uFEA8", + "/khah.medi_alefmaksura.fina": "\uFD1F", + "/khah.medi_yeh.fina": "\uFD20", + "/khaharabic": "\u062E", + "/khahfinalarabic": "\uFEA6", + "/khahinitialarabic": "\uFEA7", + "/khahmedialarabic": "\uFEA8", + "/kheicoptic": "\u03E7", + "/khhadeva": "\u0959", + "/khhagurmukhi": "\u0A59", + "/khieukhacirclekorean": "\u3278", + "/khieukhaparenkorean": "\u3218", + "/khieukhcirclekorean": "\u326A", + "/khieukhkorean": "\u314B", + "/khieukhparenkorean": "\u320A", + "/khokhaithai": "\u0E02", + "/khokhonthai": "\u0E05", + "/khokhuatthai": "\u0E03", + "/khokhwaithai": "\u0E04", + "/khomutthai": "\u0E5B", + "/khook": "\u0199", + "/khorakhangthai": "\u0E06", + "/khzfullwidth": "\u3391", + "/khzsquare": "\u3391", + "/kicirclekatakana": "\u32D6", + "/kihiragana": "\u304D", + "/kikatakana": "\u30AD", + "/kikatakanahalfwidth": "\uFF77", + "/kimono": "\u1F458", + "/kindergartenideographiccircled": "\u3245", + "/kingblack": "\u265A", + "/kingwhite": "\u2654", + "/kip": "\u20AD", + "/kiroguramusquare": "\u3315", + "/kiromeetorusquare": "\u3316", + "/kirosquare": "\u3314", + "/kirowattosquare": "\u3317", + "/kiss": "\u1F48F", + "/kissMark": "\u1F48B", + "/kissingCatFaceWithClosedEyes": "\u1F63D", + "/kissingFace": "\u1F617", + "/kissingFaceWithClosedEyes": "\u1F61A", + "/kissingFaceWithSmilingEyes": "\u1F619", + "/kiyeokacirclekorean": "\u326E", + "/kiyeokaparenkorean": "\u320E", + "/kiyeokcirclekorean": "\u3260", + "/kiyeokkorean": "\u3131", + "/kiyeokparenkorean": "\u3200", + "/kiyeoksioskorean": "\u3133", + "/kjecyr": "\u045C", + "/kjecyrillic": "\u045C", + "/kkfullwidth": "\u33CD", + "/klfullwidth": "\u3398", + "/klinebelow": "\u1E35", + "/klsquare": "\u3398", + "/km2fullwidth": "\u33A2", + "/km3fullwidth": "\u33A6", + "/kmcapitalfullwidth": "\u33CE", + "/kmcubedsquare": "\u33A6", + "/kmfullwidth": "\u339E", + "/kmonospace": "\uFF4B", + "/kmsquaredsquare": "\u33A2", + "/knda:a": "\u0C85", + "/knda:aa": "\u0C86", + "/knda:aasign": "\u0CBE", + "/knda:ai": "\u0C90", + "/knda:ailength": "\u0CD6", + "/knda:aisign": "\u0CC8", + "/knda:anusvara": "\u0C82", + "/knda:au": "\u0C94", + "/knda:ausign": "\u0CCC", + "/knda:avagraha": "\u0CBD", + "/knda:ba": "\u0CAC", + "/knda:bha": "\u0CAD", + "/knda:ca": "\u0C9A", + "/knda:cha": "\u0C9B", + "/knda:da": "\u0CA6", + "/knda:dda": "\u0CA1", + "/knda:ddha": "\u0CA2", + "/knda:dha": "\u0CA7", + "/knda:e": "\u0C8E", + "/knda:ee": "\u0C8F", + "/knda:eesign": "\u0CC7", + "/knda:eight": "\u0CEE", + "/knda:esign": "\u0CC6", + "/knda:fa": "\u0CDE", + "/knda:five": "\u0CEB", + "/knda:four": "\u0CEA", + "/knda:ga": "\u0C97", + "/knda:gha": "\u0C98", + "/knda:ha": "\u0CB9", + "/knda:i": "\u0C87", + "/knda:ii": "\u0C88", + "/knda:iisign": "\u0CC0", + "/knda:isign": "\u0CBF", + "/knda:ja": "\u0C9C", + "/knda:jha": "\u0C9D", + "/knda:jihvamuliya": "\u0CF1", + "/knda:ka": "\u0C95", + "/knda:kha": "\u0C96", + "/knda:la": "\u0CB2", + "/knda:length": "\u0CD5", + "/knda:lla": "\u0CB3", + "/knda:llvocal": "\u0CE1", + "/knda:llvocalsign": "\u0CE3", + "/knda:lvocal": "\u0C8C", + "/knda:lvocalsign": "\u0CE2", + "/knda:ma": "\u0CAE", + "/knda:na": "\u0CA8", + "/knda:nga": "\u0C99", + "/knda:nine": "\u0CEF", + "/knda:nna": "\u0CA3", + "/knda:nukta": "\u0CBC", + "/knda:nya": "\u0C9E", + "/knda:o": "\u0C92", + "/knda:one": "\u0CE7", + "/knda:oo": "\u0C93", + "/knda:oosign": "\u0CCB", + "/knda:osign": "\u0CCA", + "/knda:pa": "\u0CAA", + "/knda:pha": "\u0CAB", + "/knda:ra": "\u0CB0", + "/knda:rra": "\u0CB1", + "/knda:rrvocal": "\u0CE0", + "/knda:rrvocalsign": "\u0CC4", + "/knda:rvocal": "\u0C8B", + "/knda:rvocalsign": "\u0CC3", + "/knda:sa": "\u0CB8", + "/knda:seven": "\u0CED", + "/knda:sha": "\u0CB6", + "/knda:signcandrabindu": "\u0C81", + "/knda:signspacingcandrabindu": "\u0C80", + "/knda:six": "\u0CEC", + "/knda:ssa": "\u0CB7", + "/knda:ta": "\u0CA4", + "/knda:tha": "\u0CA5", + "/knda:three": "\u0CE9", + "/knda:tta": "\u0C9F", + "/knda:ttha": "\u0CA0", + "/knda:two": "\u0CE8", + "/knda:u": "\u0C89", + "/knda:upadhmaniya": "\u0CF2", + "/knda:usign": "\u0CC1", + "/knda:uu": "\u0C8A", + "/knda:uusign": "\u0CC2", + "/knda:va": "\u0CB5", + "/knda:virama": "\u0CCD", + "/knda:visarga": "\u0C83", + "/knda:ya": "\u0CAF", + "/knda:zero": "\u0CE6", + "/knightblack": "\u265E", + "/knightwhite": "\u2658", + "/ko:a": "\u314F", + "/ko:ae": "\u3150", + "/ko:aejungseong": "\u1162", + "/ko:aeujungseong": "\u11A3", + "/ko:ajungseong": "\u1161", + "/ko:aojungseong": "\u1176", + "/ko:araea": "\u318D", + "/ko:araeae": "\u318E", + "/ko:araeaeojungseong": "\u119F", + "/ko:araeaijungseong": "\u11A1", + "/ko:araeajungseong": "\u119E", + "/ko:araeaujungseong": "\u11A0", + "/ko:aujungseong": "\u1177", + "/ko:ceongchieumchieuchchoseong": "\u1155", + "/ko:ceongchieumcieucchoseong": "\u1150", + "/ko:ceongchieumsioschoseong": "\u113E", + "/ko:ceongchieumssangcieucchoseong": "\u1151", + "/ko:ceongchieumssangsioschoseong": "\u113F", + "/ko:chieuch": "\u314A", + "/ko:chieuchchoseong": "\u110E", + "/ko:chieuchhieuhchoseong": "\u1153", + "/ko:chieuchjongseong": "\u11BE", + "/ko:chieuchkhieukhchoseong": "\u1152", + "/ko:chitueumchieuchchoseong": "\u1154", + "/ko:chitueumcieucchoseong": "\u114E", + "/ko:chitueumsioschoseong": "\u113C", + "/ko:chitueumssangcieucchoseong": "\u114F", + "/ko:chitueumssangsioschoseong": "\u113D", + "/ko:cieuc": "\u3148", + "/ko:cieucchoseong": "\u110C", + "/ko:cieucieungchoseong": "\u114D", + "/ko:cieucjongseong": "\u11BD", + "/ko:e": "\u3154", + "/ko:ejungseong": "\u1166", + "/ko:eo": "\u3153", + "/ko:eo_eujungseong": "\u117C", + "/ko:eojungseong": "\u1165", + "/ko:eoojungseong": "\u117A", + "/ko:eoujungseong": "\u117B", + "/ko:eu": "\u3161", + "/ko:eueujungseong": "\u1196", + "/ko:eujungseong": "\u1173", + "/ko:euujungseong": "\u1195", + "/ko:filler": "\u3164", + "/ko:fillerchoseong": "\u115F", + "/ko:fillerjungseong": "\u1160", + "/ko:hieuh": "\u314E", + "/ko:hieuhchoseong": "\u1112", + "/ko:hieuhjongseong": "\u11C2", + "/ko:hieuhmieumjongseong": "\u11F7", + "/ko:hieuhnieunjongseong": "\u11F5", + "/ko:hieuhpieupjongseong": "\u11F8", + "/ko:hieuhrieuljongseong": "\u11F6", + "/ko:i": "\u3163", + "/ko:iajungseong": "\u1198", + "/ko:iaraeajungseong": "\u119D", + "/ko:ieujungseong": "\u119C", + "/ko:ieung": "\u3147", + "/ko:ieungchieuchchoseong": "\u1149", + "/ko:ieungchoseong": "\u110B", + "/ko:ieungcieucchoseong": "\u1148", + "/ko:ieungjongseong": "\u11BC", + "/ko:ieungkhieukhjongseong": "\u11EF", + "/ko:ieungkiyeokchoseong": "\u1141", + "/ko:ieungkiyeokjongseong": "\u11EC", + "/ko:ieungmieumchoseong": "\u1143", + "/ko:ieungpansioschoseong": "\u1146", + "/ko:ieungphieuphchoseong": "\u114B", + "/ko:ieungpieupchoseong": "\u1144", + "/ko:ieungsioschoseong": "\u1145", + "/ko:ieungssangkiyeokjongseong": "\u11ED", + "/ko:ieungthieuthchoseong": "\u114A", + "/ko:ieungtikeutchoseong": "\u1142", + "/ko:ijungseong": "\u1175", + "/ko:iojungseong": "\u119A", + "/ko:iujungseong": "\u119B", + "/ko:iyajungseong": "\u1199", + "/ko:kapyeounmieum": "\u3171", + "/ko:kapyeounmieumchoseong": "\u111D", + "/ko:kapyeounmieumjongseong": "\u11E2", + "/ko:kapyeounphieuph": "\u3184", + "/ko:kapyeounphieuphchoseong": "\u1157", + "/ko:kapyeounphieuphjongseong": "\u11F4", + "/ko:kapyeounpieup": "\u3178", + "/ko:kapyeounpieupchoseong": "\u112B", + "/ko:kapyeounpieupjongseong": "\u11E6", + "/ko:kapyeounrieulchoseong": "\u111B", + "/ko:kapyeounssangpieup": "\u3179", + "/ko:kapyeounssangpieupchoseong": "\u112C", + "/ko:khieukh": "\u314B", + "/ko:khieukhchoseong": "\u110F", + "/ko:khieukhjongseong": "\u11BF", + "/ko:kiyeok": "\u3131", + "/ko:kiyeokchieuchjongseong": "\u11FC", + "/ko:kiyeokchoseong": "\u1100", + "/ko:kiyeokhieuhjongseong": "\u11FE", + "/ko:kiyeokjongseong": "\u11A8", + "/ko:kiyeokkhieukhjongseong": "\u11FD", + "/ko:kiyeoknieunjongseong": "\u11FA", + "/ko:kiyeokpieupjongseong": "\u11FB", + "/ko:kiyeokrieuljongseong": "\u11C3", + "/ko:kiyeoksios": "\u3133", + "/ko:kiyeoksiosjongseong": "\u11AA", + "/ko:kiyeoksioskiyeokjongseong": "\u11C4", + "/ko:kiyeoktikeutchoseong": "\u115A", + "/ko:mieum": "\u3141", + "/ko:mieumchieuchjongseong": "\u11E0", + "/ko:mieumchoseong": "\u1106", + "/ko:mieumhieuhjongseong": "\u11E1", + "/ko:mieumjongseong": "\u11B7", + "/ko:mieumkiyeokjongseong": "\u11DA", + "/ko:mieumpansios": "\u3170", + "/ko:mieumpansiosjongseong": "\u11DF", + "/ko:mieumpieup": "\u316E", + "/ko:mieumpieupchoseong": "\u111C", + "/ko:mieumpieupjongseong": "\u11DC", + "/ko:mieumrieuljongseong": "\u11DB", + "/ko:mieumsios": "\u316F", + "/ko:mieumsiosjongseong": "\u11DD", + "/ko:mieumssangsiosjongseong": "\u11DE", + "/ko:nieun": "\u3134", + "/ko:nieunchoseong": "\u1102", + "/ko:nieuncieuc": "\u3135", + "/ko:nieuncieucchoseong": "\u115C", + "/ko:nieuncieucjongseong": "\u11AC", + "/ko:nieunhieuh": "\u3136", + "/ko:nieunhieuhchoseong": "\u115D", + "/ko:nieunhieuhjongseong": "\u11AD", + "/ko:nieunjongseong": "\u11AB", + "/ko:nieunkiyeokchoseong": "\u1113", + "/ko:nieunkiyeokjongseong": "\u11C5", + "/ko:nieunpansios": "\u3168", + "/ko:nieunpansiosjongseong": "\u11C8", + "/ko:nieunpieupchoseong": "\u1116", + "/ko:nieunsios": "\u3167", + "/ko:nieunsioschoseong": "\u115B", + "/ko:nieunsiosjongseong": "\u11C7", + "/ko:nieunthieuthjongseong": "\u11C9", + "/ko:nieuntikeut": "\u3166", + "/ko:nieuntikeutchoseong": "\u1115", + "/ko:nieuntikeutjongseong": "\u11C6", + "/ko:o": "\u3157", + "/ko:o_ejungseong": "\u1180", + "/ko:o_eojungseong": "\u117F", + "/ko:oe": "\u315A", + "/ko:oejungseong": "\u116C", + "/ko:ojungseong": "\u1169", + "/ko:oojungseong": "\u1182", + "/ko:oujungseong": "\u1183", + "/ko:oyaejungseong": "\u11A7", + "/ko:oyajungseong": "\u11A6", + "/ko:oyejungseong": "\u1181", + "/ko:pansios": "\u317F", + "/ko:pansioschoseong": "\u1140", + "/ko:pansiosjongseong": "\u11EB", + "/ko:phieuph": "\u314D", + "/ko:phieuphchoseong": "\u1111", + "/ko:phieuphjongseong": "\u11C1", + "/ko:phieuphpieupchoseong": "\u1156", + "/ko:phieuphpieupjongseong": "\u11F3", + "/ko:pieup": "\u3142", + "/ko:pieupchieuchchoseong": "\u1128", + "/ko:pieupchoseong": "\u1107", + "/ko:pieupcieuc": "\u3176", + "/ko:pieupcieucchoseong": "\u1127", + "/ko:pieuphieuhjongseong": "\u11E5", + "/ko:pieupjongseong": "\u11B8", + "/ko:pieupkiyeok": "\u3172", + "/ko:pieupkiyeokchoseong": "\u111E", + "/ko:pieupnieunchoseong": "\u111F", + "/ko:pieupphieuphchoseong": "\u112A", + "/ko:pieupphieuphjongseong": "\u11E4", + "/ko:pieuprieuljongseong": "\u11E3", + "/ko:pieupsios": "\u3144", + "/ko:pieupsioschoseong": "\u1121", + "/ko:pieupsioscieucchoseong": "\u1126", + "/ko:pieupsiosjongseong": "\u11B9", + "/ko:pieupsioskiyeok": "\u3174", + "/ko:pieupsioskiyeokchoseong": "\u1122", + "/ko:pieupsiospieupchoseong": "\u1124", + "/ko:pieupsiostikeut": "\u3175", + "/ko:pieupsiostikeutchoseong": "\u1123", + "/ko:pieupssangsioschoseong": "\u1125", + "/ko:pieupthieuth": "\u3177", + "/ko:pieupthieuthchoseong": "\u1129", + "/ko:pieuptikeut": "\u3173", + "/ko:pieuptikeutchoseong": "\u1120", + "/ko:rieul": "\u3139", + "/ko:rieulchoseong": "\u1105", + "/ko:rieulhieuh": "\u3140", + "/ko:rieulhieuhchoseong": "\u111A", + "/ko:rieulhieuhjongseong": "\u11B6", + "/ko:rieuljongseong": "\u11AF", + "/ko:rieulkapyeounpieupjongseong": "\u11D5", + "/ko:rieulkhieukhjongseong": "\u11D8", + "/ko:rieulkiyeok": "\u313A", + "/ko:rieulkiyeokjongseong": "\u11B0", + "/ko:rieulkiyeoksios": "\u3169", + "/ko:rieulkiyeoksiosjongseong": "\u11CC", + "/ko:rieulmieum": "\u313B", + "/ko:rieulmieumjongseong": "\u11B1", + "/ko:rieulmieumkiyeokjongseong": "\u11D1", + "/ko:rieulmieumsiosjongseong": "\u11D2", + "/ko:rieulnieunchoseong": "\u1118", + "/ko:rieulnieunjongseong": "\u11CD", + "/ko:rieulpansios": "\u316C", + "/ko:rieulpansiosjongseong": "\u11D7", + "/ko:rieulphieuph": "\u313F", + "/ko:rieulphieuphjongseong": "\u11B5", + "/ko:rieulpieup": "\u313C", + "/ko:rieulpieuphieuhjongseong": "\u11D4", + "/ko:rieulpieupjongseong": "\u11B2", + "/ko:rieulpieupsios": "\u316B", + "/ko:rieulpieupsiosjongseong": "\u11D3", + "/ko:rieulsios": "\u313D", + "/ko:rieulsiosjongseong": "\u11B3", + "/ko:rieulssangsiosjongseong": "\u11D6", + "/ko:rieulthieuth": "\u313E", + "/ko:rieulthieuthjongseong": "\u11B4", + "/ko:rieultikeut": "\u316A", + "/ko:rieultikeuthieuhjongseong": "\u11CF", + "/ko:rieultikeutjongseong": "\u11CE", + "/ko:rieulyeorinhieuh": "\u316D", + "/ko:rieulyeorinhieuhjongseong": "\u11D9", + "/ko:sios": "\u3145", + "/ko:sioschieuchchoseong": "\u1137", + "/ko:sioschoseong": "\u1109", + "/ko:sioscieuc": "\u317E", + "/ko:sioscieucchoseong": "\u1136", + "/ko:sioshieuhchoseong": "\u113B", + "/ko:siosieungchoseong": "\u1135", + "/ko:siosjongseong": "\u11BA", + "/ko:sioskhieukhchoseong": "\u1138", + "/ko:sioskiyeok": "\u317A", + "/ko:sioskiyeokchoseong": "\u112D", + "/ko:sioskiyeokjongseong": "\u11E7", + "/ko:siosmieumchoseong": "\u1131", + "/ko:siosnieun": "\u317B", + "/ko:siosnieunchoseong": "\u112E", + "/ko:siosphieuphchoseong": "\u113A", + "/ko:siospieup": "\u317D", + "/ko:siospieupchoseong": "\u1132", + "/ko:siospieupjongseong": "\u11EA", + "/ko:siospieupkiyeokchoseong": "\u1133", + "/ko:siosrieulchoseong": "\u1130", + "/ko:siosrieuljongseong": "\u11E9", + "/ko:siosssangsioschoseong": "\u1134", + "/ko:siosthieuthchoseong": "\u1139", + "/ko:siostikeut": "\u317C", + "/ko:siostikeutchoseong": "\u112F", + "/ko:siostikeutjongseong": "\u11E8", + "/ko:ssangaraeajungseong": "\u11A2", + "/ko:ssangcieuc": "\u3149", + "/ko:ssangcieucchoseong": "\u110D", + "/ko:ssanghieuh": "\u3185", + "/ko:ssanghieuhchoseong": "\u1158", + "/ko:ssangieung": "\u3180", + "/ko:ssangieungchoseong": "\u1147", + "/ko:ssangieungjongseong": "\u11EE", + "/ko:ssangkiyeok": "\u3132", + "/ko:ssangkiyeokchoseong": "\u1101", + "/ko:ssangkiyeokjongseong": "\u11A9", + "/ko:ssangnieun": "\u3165", + "/ko:ssangnieunchoseong": "\u1114", + "/ko:ssangnieunjongseong": "\u11FF", + "/ko:ssangpieup": "\u3143", + "/ko:ssangpieupchoseong": "\u1108", + "/ko:ssangrieulchoseong": "\u1119", + "/ko:ssangrieuljongseong": "\u11D0", + "/ko:ssangsios": "\u3146", + "/ko:ssangsioschoseong": "\u110A", + "/ko:ssangsiosjongseong": "\u11BB", + "/ko:ssangtikeut": "\u3138", + "/ko:ssangtikeutchoseong": "\u1104", + "/ko:thieuth": "\u314C", + "/ko:thieuthchoseong": "\u1110", + "/ko:thieuthjongseong": "\u11C0", + "/ko:tikeut": "\u3137", + "/ko:tikeutchoseong": "\u1103", + "/ko:tikeutjongseong": "\u11AE", + "/ko:tikeutkiyeokchoseong": "\u1117", + "/ko:tikeutkiyeokjongseong": "\u11CA", + "/ko:tikeutrieulchoseong": "\u115E", + "/ko:tikeutrieuljongseong": "\u11CB", + "/ko:u": "\u315C", + "/ko:uaejungseong": "\u118A", + "/ko:uajungseong": "\u1189", + "/ko:ueo_eujungseong": "\u118B", + "/ko:ujungseong": "\u116E", + "/ko:uujungseong": "\u118D", + "/ko:uyejungseong": "\u118C", + "/ko:wa": "\u3158", + "/ko:wae": "\u3159", + "/ko:waejungseong": "\u116B", + "/ko:wajungseong": "\u116A", + "/ko:we": "\u315E", + "/ko:wejungseong": "\u1170", + "/ko:weo": "\u315D", + "/ko:weojungseong": "\u116F", + "/ko:wi": "\u315F", + "/ko:wijungseong": "\u1171", + "/ko:ya": "\u3151", + "/ko:yae": "\u3152", + "/ko:yaejungseong": "\u1164", + "/ko:yajungseong": "\u1163", + "/ko:yaojungseong": "\u1178", + "/ko:yaujungseong": "\u11A4", + "/ko:yayojungseong": "\u1179", + "/ko:ye": "\u3156", + "/ko:yejungseong": "\u1168", + "/ko:yeo": "\u3155", + "/ko:yeojungseong": "\u1167", + "/ko:yeoojungseong": "\u117D", + "/ko:yeorinhieuh": "\u3186", + "/ko:yeorinhieuhchoseong": "\u1159", + "/ko:yeorinhieuhjongseong": "\u11F9", + "/ko:yeoujungseong": "\u117E", + "/ko:yeoyajungseong": "\u11A5", + "/ko:yesieung": "\u3181", + "/ko:yesieungchoseong": "\u114C", + "/ko:yesieungjongseong": "\u11F0", + "/ko:yesieungpansios": "\u3183", + "/ko:yesieungpansiosjongseong": "\u11F2", + "/ko:yesieungsios": "\u3182", + "/ko:yesieungsiosjongseong": "\u11F1", + "/ko:yi": "\u3162", + "/ko:yijungseong": "\u1174", + "/ko:yiujungseong": "\u1197", + "/ko:yo": "\u315B", + "/ko:yoi": "\u3189", + "/ko:yoijungseong": "\u1188", + "/ko:yojungseong": "\u116D", + "/ko:yoojungseong": "\u1187", + "/ko:yoya": "\u3187", + "/ko:yoyae": "\u3188", + "/ko:yoyaejungseong": "\u1185", + "/ko:yoyajungseong": "\u1184", + "/ko:yoyeojungseong": "\u1186", + "/ko:yu": "\u3160", + "/ko:yuajungseong": "\u118E", + "/ko:yuejungseong": "\u1190", + "/ko:yueojungseong": "\u118F", + "/ko:yui": "\u318C", + "/ko:yuijungseong": "\u1194", + "/ko:yujungseong": "\u1172", + "/ko:yuujungseong": "\u1193", + "/ko:yuye": "\u318B", + "/ko:yuyejungseong": "\u1192", + "/ko:yuyeo": "\u318A", + "/ko:yuyeojungseong": "\u1191", + "/koala": "\u1F428", + "/kobliquestroke": "\uA7A3", + "/kocirclekatakana": "\u32D9", + "/kohiragana": "\u3053", + "/kohmfullwidth": "\u33C0", + "/kohmsquare": "\u33C0", + "/kokaithai": "\u0E01", + "/kokatakana": "\u30B3", + "/kokatakanahalfwidth": "\uFF7A", + "/kooposquare": "\u331E", + "/koppa": "\u03DF", + "/koppaarchaic": "\u03D9", + "/koppacyr": "\u0481", + "/koppacyrillic": "\u0481", + "/koreanstandardsymbol": "\u327F", + "/koroniscmb": "\u0343", + "/korunasquare": "\u331D", + "/kotoideographiccircled": "\u3247", + "/kpafullwidth": "\u33AA", + "/kparen": "\u24A6", + "/kparenthesized": "\u24A6", + "/kpasquare": "\u33AA", + "/kra": "\u0138", + "/ksicyr": "\u046F", + "/ksicyrillic": "\u046F", + "/kstroke": "\uA741", + "/kstrokediagonalstroke": "\uA745", + "/ktfullwidth": "\u33CF", + "/ktsquare": "\u33CF", + "/kturned": "\u029E", + "/kucirclekatakana": "\u32D7", + "/kuhiragana": "\u304F", + "/kukatakana": "\u30AF", + "/kukatakanahalfwidth": "\uFF78", + "/kuroonesquare": "\u331B", + "/kuruzeirosquare": "\u331A", + "/kvfullwidth": "\u33B8", + "/kvsquare": "\u33B8", + "/kwfullwidth": "\u33BE", + "/kwsquare": "\u33BE", + "/kyuriisquare": "\u3312", + "/l": "\u006C", + "/l.inferior": "\u2097", + "/label": "\u1F3F7", + "/labengali": "\u09B2", + "/laborideographiccircled": "\u3298", + "/laborideographicparen": "\u3238", + "/lacute": "\u013A", + "/ladeva": "\u0932", + "/ladyBeetle": "\u1F41E", + "/lagujarati": "\u0AB2", + "/lagurmukhi": "\u0A32", + "/lakkhangyaothai": "\u0E45", + "/lam": "\u0644", + "/lam.fina": "\uFEDE", + "/lam.init": "\uFEDF", + "/lam.init_alef.fina": "\uFEFB", + "/lam.init_alef.medi_hamzaabove.fina": "\uFEF7", + "/lam.init_alef.medi_hamzabelow.fina": "\uFEF9", + "/lam.init_alef.medi_maddaabove.fina": "\uFEF5", + "/lam.init_alefmaksura.fina": "\uFC43", + "/lam.init_hah.fina": "\uFC40", + "/lam.init_hah.medi": "\uFCCA", + "/lam.init_hah.medi_meem.medi": "\uFDB5", + "/lam.init_heh.medi": "\uFCCD", + "/lam.init_jeem.fina": "\uFC3F", + "/lam.init_jeem.medi": "\uFCC9", + "/lam.init_jeem.medi_jeem.medi": "\uFD83", + "/lam.init_jeem.medi_meem.medi": "\uFDBA", + "/lam.init_khah.fina": "\uFC41", + "/lam.init_khah.medi": "\uFCCB", + "/lam.init_khah.medi_meem.medi": "\uFD86", + "/lam.init_meem.fina": "\uFC42", + "/lam.init_meem.medi": "\uFCCC", + "/lam.init_meem.medi_hah.medi": "\uFD88", + "/lam.init_yeh.fina": "\uFC44", + "/lam.isol": "\uFEDD", + "/lam.medi": "\uFEE0", + "/lam.medi_alef.fina": "\uFEFC", + "/lam.medi_alef.medi_hamzaabove.fina": "\uFEF8", + "/lam.medi_alef.medi_hamzabelow.fina": "\uFEFA", + "/lam.medi_alef.medi_maddaabove.fina": "\uFEF6", + "/lam.medi_alefmaksura.fina": "\uFC86", + "/lam.medi_hah.medi_alefmaksura.fina": "\uFD82", + "/lam.medi_hah.medi_meem.fina": "\uFD80", + "/lam.medi_hah.medi_yeh.fina": "\uFD81", + "/lam.medi_jeem.medi_jeem.fina": "\uFD84", + "/lam.medi_jeem.medi_meem.fina": "\uFDBC", + "/lam.medi_jeem.medi_yeh.fina": "\uFDAC", + "/lam.medi_khah.medi_meem.fina": "\uFD85", + "/lam.medi_meem.fina": "\uFC85", + "/lam.medi_meem.medi": "\uFCED", + "/lam.medi_meem.medi_hah.fina": "\uFD87", + "/lam.medi_meem.medi_yeh.fina": "\uFDAD", + "/lam.medi_yeh.fina": "\uFC87", + "/lamBar": "\u076A", + "/lamVabove": "\u06B5", + "/lamalefabove": "\u06D9", + "/lamaleffinalarabic": "\uFEFC", + "/lamalefhamzaabovefinalarabic": "\uFEF8", + "/lamalefhamzaaboveisolatedarabic": "\uFEF7", + "/lamalefhamzabelowfinalarabic": "\uFEFA", + "/lamalefhamzabelowisolatedarabic": "\uFEF9", + "/lamalefisolatedarabic": "\uFEFB", + "/lamalefmaddaabovefinalarabic": "\uFEF6", + "/lamalefmaddaaboveisolatedarabic": "\uFEF5", + "/lamarabic": "\u0644", + "/lambda": "\u03BB", + "/lambdastroke": "\u019B", + "/lamdotabove": "\u06B6", + "/lamed": "\u05DC", + "/lamed:hb": "\u05DC", + "/lameddagesh": "\uFB3C", + "/lameddageshhebrew": "\uFB3C", + "/lamedhebrew": "\u05DC", + "/lamedholam": "\u05DC", + "/lamedholamdagesh": "\u05DC", + "/lamedholamdageshhebrew": "\u05DC", + "/lamedholamhebrew": "\u05DC", + "/lamedwide:hb": "\uFB25", + "/lamedwithdagesh:hb": "\uFB3C", + "/lamfinalarabic": "\uFEDE", + "/lamhahinitialarabic": "\uFCCA", + "/laminitialarabic": "\uFEDF", + "/lamjeeminitialarabic": "\uFCC9", + "/lamkhahinitialarabic": "\uFCCB", + "/lamlamhehisolatedarabic": "\uFDF2", + "/lammedialarabic": "\uFEE0", + "/lammeemhahinitialarabic": "\uFD88", + "/lammeeminitialarabic": "\uFCCC", + "/lammeemjeeminitialarabic": "\uFEDF", + "/lammeemkhahinitialarabic": "\uFEDF", + "/lamthreedotsabove": "\u06B7", + "/lamthreedotsbelow": "\u06B8", + "/lanemergeleftblack": "\u26D8", + "/lanemergeleftwhite": "\u26D9", + "/largeBlueCircle": "\u1F535", + "/largeBlueDiamond": "\u1F537", + "/largeOrangeDiamond": "\u1F536", + "/largeRedCircle": "\u1F534", + "/largecircle": "\u25EF", + "/largetackdown": "\u27D9", + "/largetackup": "\u27D8", + "/lari": "\u20BE", + "/lastQuarterMoon": "\u1F317", + "/lastQuarterMoonFace": "\u1F31C", + "/lastquartermoon": "\u263E", + "/layar": "\uA982", + "/lazysinverted": "\u223E", + "/lbar": "\u019A", + "/lbbar": "\u2114", + "/lbelt": "\u026C", + "/lbeltretroflex": "\uA78E", + "/lbopomofo": "\u310C", + "/lbroken": "\uA747", + "/lcaron": "\u013E", + "/lcedilla": "\u013C", + "/lcircle": "\u24DB", + "/lcircumflexbelow": "\u1E3D", + "/lcommaaccent": "\u013C", + "/lcurl": "\u0234", + "/ldblbar": "\u2C61", + "/ldot": "\u0140", + "/ldotaccent": "\u0140", + "/ldotbelow": "\u1E37", + "/ldotbelowmacron": "\u1E39", + "/leafFlutteringInWind": "\u1F343", + "/ledger": "\u1F4D2", + "/left-pointingMagnifyingGlass": "\u1F50D", + "/leftAngerBubble": "\u1F5EE", + "/leftFiveEighthsBlock": "\u258B", + "/leftHalfBlock": "\u258C", + "/leftHandTelephoneReceiver": "\u1F57B", + "/leftLuggage": "\u1F6C5", + "/leftOneEighthBlock": "\u258F", + "/leftOneQuarterBlock": "\u258E", + "/leftSevenEighthsBlock": "\u2589", + "/leftSpeechBubble": "\u1F5E8", + "/leftThoughtBubble": "\u1F5EC", + "/leftThreeEighthsBlock": "\u258D", + "/leftThreeQuartersBlock": "\u258A", + "/leftWritingHand": "\u1F58E", + "/leftangleabovecmb": "\u031A", + "/leftarrowoverrightarrow": "\u21C6", + "/leftdnheavyrightuplight": "\u2545", + "/leftharpoonoverrightharpoon": "\u21CB", + "/leftheavyrightdnlight": "\u252D", + "/leftheavyrightuplight": "\u2535", + "/leftheavyrightvertlight": "\u253D", + "/leftideographiccircled": "\u32A7", + "/leftlightrightdnheavy": "\u2532", + "/leftlightrightupheavy": "\u253A", + "/leftlightrightvertheavy": "\u254A", + "/lefttackbelowcmb": "\u0318", + "/lefttorightembed": "\u202A", + "/lefttorightisolate": "\u2066", + "/lefttorightmark": "\u200E", + "/lefttorightoverride": "\u202D", + "/leftupheavyrightdnlight": "\u2543", + "/lemon": "\u1F34B", + "/lenis": "\u1FBF", + "/lenisacute": "\u1FCE", + "/lenisgrave": "\u1FCD", + "/lenistilde": "\u1FCF", + "/leo": "\u264C", + "/leopard": "\u1F406", + "/less": "\u003C", + "/lessbutnotequal": "\u2268", + "/lessbutnotequivalent": "\u22E6", + "/lessdot": "\u22D6", + "/lessequal": "\u2264", + "/lessequalorgreater": "\u22DA", + "/lessmonospace": "\uFF1C", + "/lessorequivalent": "\u2272", + "/lessorgreater": "\u2276", + "/lessoverequal": "\u2266", + "/lesssmall": "\uFE64", + "/levelSlider": "\u1F39A", + "/lezh": "\u026E", + "/lfblock": "\u258C", + "/lhacyr": "\u0515", + "/lhookretroflex": "\u026D", + "/libra": "\u264E", + "/ligaturealeflamed:hb": "\uFB4F", + "/ligatureoemod": "\uA7F9", + "/lightCheckMark": "\u1F5F8", + "/lightRail": "\u1F688", + "/lightShade": "\u2591", + "/lightarcdnleft": "\u256E", + "/lightarcdnright": "\u256D", + "/lightarcupleft": "\u256F", + "/lightarcupright": "\u2570", + "/lightdbldashhorz": "\u254C", + "/lightdbldashvert": "\u254E", + "/lightdiagcross": "\u2573", + "/lightdiagupleftdnright": "\u2572", + "/lightdiaguprightdnleft": "\u2571", + "/lightdn": "\u2577", + "/lightdnhorz": "\u252C", + "/lightdnleft": "\u2510", + "/lightdnright": "\u250C", + "/lighthorz": "\u2500", + "/lightleft": "\u2574", + "/lightleftheavyright": "\u257C", + "/lightning": "\u2607", + "/lightningMood": "\u1F5F2", + "/lightningMoodBubble": "\u1F5F1", + "/lightquaddashhorz": "\u2508", + "/lightquaddashvert": "\u250A", + "/lightright": "\u2576", + "/lighttrpldashhorz": "\u2504", + "/lighttrpldashvert": "\u2506", + "/lightup": "\u2575", + "/lightupheavydn": "\u257D", + "/lightuphorz": "\u2534", + "/lightupleft": "\u2518", + "/lightupright": "\u2514", + "/lightvert": "\u2502", + "/lightverthorz": "\u253C", + "/lightvertleft": "\u2524", + "/lightvertright": "\u251C", + "/lineextensionhorizontal": "\u23AF", + "/lineextensionvertical": "\u23D0", + "/linemiddledotvertical": "\u237F", + "/lineseparator": "\u2028", + "/lingsapada": "\uA9C8", + "/link": "\u1F517", + "/linkedPaperclips": "\u1F587", + "/lips": "\u1F5E2", + "/lipstick": "\u1F484", + "/lira": "\u20A4", + "/litre": "\u2113", + "/livretournois": "\u20B6", + "/liwnarmenian": "\u056C", + "/lj": "\u01C9", + "/ljecyr": "\u0459", + "/ljecyrillic": "\u0459", + "/ljekomicyr": "\u0509", + "/ll": "\uF6C0", + "/lladeva": "\u0933", + "/llagujarati": "\u0AB3", + "/llinebelow": "\u1E3B", + "/llladeva": "\u0934", + "/llvocalicbengali": "\u09E1", + "/llvocalicdeva": "\u0961", + "/llvocalicvowelsignbengali": "\u09E3", + "/llvocalicvowelsigndeva": "\u0963", + "/llwelsh": "\u1EFB", + "/lmacrondot": "\u1E39", + "/lmfullwidth": "\u33D0", + "/lmiddletilde": "\u026B", + "/lmonospace": "\uFF4C", + "/lmsquare": "\u33D0", + "/lnfullwidth": "\u33D1", + "/lochulathai": "\u0E2C", + "/lock": "\u1F512", + "/lockInkPen": "\u1F50F", + "/logfullwidth": "\u33D2", + "/logicaland": "\u2227", + "/logicalandarray": "\u22C0", + "/logicalnot": "\u00AC", + "/logicalnotreversed": "\u2310", + "/logicalor": "\u2228", + "/logicalorarray": "\u22C1", + "/lolingthai": "\u0E25", + "/lollipop": "\u1F36D", + "/longdivision": "\u27CC", + "/longovershortmetrical": "\u23D2", + "/longovertwoshortsmetrical": "\u23D4", + "/longs": "\u017F", + "/longs_t": "\uFB05", + "/longsdot": "\u1E9B", + "/longswithdiagonalstroke": "\u1E9C", + "/longswithhighstroke": "\u1E9D", + "/longtackleft": "\u27DE", + "/longtackright": "\u27DD", + "/losslesssquare": "\u1F1A9", + "/loudlyCryingFace": "\u1F62D", + "/loveHotel": "\u1F3E9", + "/loveLetter": "\u1F48C", + "/lowBrightness": "\u1F505", + "/lowasterisk": "\u204E", + "/lowerFiveEighthsBlock": "\u2585", + "/lowerHalfBlock": "\u2584", + "/lowerLeftBallpointPen": "\u1F58A", + "/lowerLeftCrayon": "\u1F58D", + "/lowerLeftFountainPen": "\u1F58B", + "/lowerLeftPaintbrush": "\u1F58C", + "/lowerLeftPencil": "\u1F589", + "/lowerOneEighthBlock": "\u2581", + "/lowerOneQuarterBlock": "\u2582", + "/lowerRightShadowedWhiteCircle": "\u1F53E", + "/lowerSevenEighthsBlock": "\u2587", + "/lowerThreeEighthsBlock": "\u2583", + "/lowerThreeQuartersBlock": "\u2586", + "/lowercornerdotright": "\u27D3", + "/lowerhalfcircle": "\u25E1", + "/lowerhalfcircleinversewhite": "\u25DB", + "/lowerquadrantcirculararcleft": "\u25DF", + "/lowerquadrantcirculararcright": "\u25DE", + "/lowertriangleleft": "\u25FA", + "/lowertriangleleftblack": "\u25E3", + "/lowertriangleright": "\u25FF", + "/lowertrianglerightblack": "\u25E2", + "/lowideographiccircled": "\u32A6", + "/lowlinecenterline": "\uFE4E", + "/lowlinecmb": "\u0332", + "/lowlinedashed": "\uFE4D", + "/lownumeralsign": "\u0375", + "/lowquotedblprime": "\u301F", + "/lozenge": "\u25CA", + "/lozengedividedbyrulehorizontal": "\u27E0", + "/lozengesquare": "\u2311", + "/lparen": "\u24A7", + "/lparenthesized": "\u24A7", + "/lretroflex": "\u026D", + "/ls": "\u02AA", + "/lslash": "\u0142", + "/lsquare": "\u2113", + "/lstroke": "\uA749", + "/lsuperior": "\uF6EE", + "/lsupmod": "\u02E1", + "/lt:Alpha": "\u2C6D", + "/lt:Alphaturned": "\u2C70", + "/lt:Beta": "\uA7B4", + "/lt:Chi": "\uA7B3", + "/lt:Gamma": "\u0194", + "/lt:Iota": "\u0196", + "/lt:Omega": "\uA7B6", + "/lt:Upsilon": "\u01B1", + "/lt:beta": "\uA7B5", + "/lt:delta": "\u1E9F", + "/lt:omega": "\uA7B7", + "/ltshade": "\u2591", + "/lttr:bet": "\u2136", + "/lttr:dalet": "\u2138", + "/lttr:gimel": "\u2137", + "/lttr:gscript": "\u210A", + "/lturned": "\uA781", + "/ltypeopencircuit": "\u2390", + "/luhurpada": "\uA9C5", + "/lum": "\uA772", + "/lungsipada": "\uA9C9", + "/luthai": "\u0E26", + "/lvocalicbengali": "\u098C", + "/lvocalicdeva": "\u090C", + "/lvocalicvowelsignbengali": "\u09E2", + "/lvocalicvowelsigndeva": "\u0962", + "/lxfullwidth": "\u33D3", + "/lxsquare": "\u33D3", + "/lzed": "\u02AB", + "/m": "\u006D", + "/m.inferior": "\u2098", + "/m2fullwidth": "\u33A1", + "/m3fullwidth": "\u33A5", + "/mabengali": "\u09AE", + "/macirclekatakana": "\u32EE", + "/macron": "\u00AF", + "/macronbelowcmb": "\u0331", + "/macroncmb": "\u0304", + "/macronlowmod": "\u02CD", + "/macronmod": "\u02C9", + "/macronmonospace": "\uFFE3", + "/macute": "\u1E3F", + "/madda": "\u0653", + "/maddaabove": "\u06E4", + "/madeva": "\u092E", + "/madyapada": "\uA9C4", + "/mafullwidth": "\u3383", + "/magujarati": "\u0AAE", + "/magurmukhi": "\u0A2E", + "/mahapakhhebrew": "\u05A4", + "/mahapakhlefthebrew": "\u05A4", + "/mahhasquare": "\u3345", + "/mahiragana": "\u307E", + "/mahpach:hb": "\u05A4", + "/maichattawalowleftthai": "\uF895", + "/maichattawalowrightthai": "\uF894", + "/maichattawathai": "\u0E4B", + "/maichattawaupperleftthai": "\uF893", + "/maieklowleftthai": "\uF88C", + "/maieklowrightthai": "\uF88B", + "/maiekthai": "\u0E48", + "/maiekupperleftthai": "\uF88A", + "/maihanakatleftthai": "\uF884", + "/maihanakatthai": "\u0E31", + "/maikurosquare": "\u3343", + "/mairusquare": "\u3344", + "/maitaikhuleftthai": "\uF889", + "/maitaikhuthai": "\u0E47", + "/maitholowleftthai": "\uF88F", + "/maitholowrightthai": "\uF88E", + "/maithothai": "\u0E49", + "/maithoupperleftthai": "\uF88D", + "/maitrilowleftthai": "\uF892", + "/maitrilowrightthai": "\uF891", + "/maitrithai": "\u0E4A", + "/maitriupperleftthai": "\uF890", + "/maiyamokthai": "\u0E46", + "/makatakana": "\u30DE", + "/makatakanahalfwidth": "\uFF8F", + "/male": "\u2642", + "/malefemale": "\u26A5", + "/maleideographiccircled": "\u329A", + "/malestroke": "\u26A6", + "/malestrokemalefemale": "\u26A7", + "/man": "\u1F468", + "/manAndWomanHoldingHands": "\u1F46B", + "/manDancing": "\u1F57A", + "/manGuaPiMao": "\u1F472", + "/manInBusinessSuitLevitating": "\u1F574", + "/manTurban": "\u1F473", + "/manat": "\u20BC", + "/mansShoe": "\u1F45E", + "/mansyonsquare": "\u3347", + "/mantelpieceClock": "\u1F570", + "/mapleLeaf": "\u1F341", + "/maplighthouse": "\u26EF", + "/maqaf:hb": "\u05BE", + "/maqafhebrew": "\u05BE", + "/marchtelegraph": "\u32C2", + "/mark": "\u061C", + "/markerdottedraisedinterpolation": "\u2E07", + "/markerdottedtransposition": "\u2E08", + "/markerraisedinterpolation": "\u2E06", + "/marknoonghunna": "\u0658", + "/marksChapter": "\u1F545", + "/marriage": "\u26AD", + "/mars": "\u2642", + "/marukusquare": "\u3346", + "/masoraCircle:hb": "\u05AF", + "/masoracirclehebrew": "\u05AF", + "/masquare": "\u3383", + "/masumark": "\u303C", + "/math:bowtie": "\u22C8", + "/math:cuberoot": "\u221B", + "/math:fourthroot": "\u221C", + "/maximize": "\u1F5D6", + "/maytelegraph": "\u32C4", + "/mbfullwidth": "\u3386", + "/mbopomofo": "\u3107", + "/mbsmallfullwidth": "\u33D4", + "/mbsquare": "\u33D4", + "/mcircle": "\u24DC", + "/mcubedsquare": "\u33A5", + "/mdot": "\u1E41", + "/mdotaccent": "\u1E41", + "/mdotbelow": "\u1E43", + "/measuredangle": "\u2221", + "/measuredby": "\u225E", + "/meatOnBone": "\u1F356", + "/mecirclekatakana": "\u32F1", + "/medicineideographiccircled": "\u32A9", + "/mediumShade": "\u2592", + "/mediumcircleblack": "\u26AB", + "/mediumcirclewhite": "\u26AA", + "/mediummathematicalspace": "\u205F", + "/mediumsmallcirclewhite": "\u26AC", + "/meem": "\u0645", + "/meem.fina": "\uFEE2", + "/meem.init": "\uFEE3", + "/meem.init_alefmaksura.fina": "\uFC49", + "/meem.init_hah.fina": "\uFC46", + "/meem.init_hah.medi": "\uFCCF", + "/meem.init_hah.medi_jeem.medi": "\uFD89", + "/meem.init_hah.medi_meem.medi": "\uFD8A", + "/meem.init_jeem.fina": "\uFC45", + "/meem.init_jeem.medi": "\uFCCE", + "/meem.init_jeem.medi_hah.medi": "\uFD8C", + "/meem.init_jeem.medi_khah.medi": "\uFD92", + "/meem.init_jeem.medi_meem.medi": "\uFD8D", + "/meem.init_khah.fina": "\uFC47", + "/meem.init_khah.medi": "\uFCD0", + "/meem.init_khah.medi_jeem.medi": "\uFD8E", + "/meem.init_khah.medi_meem.medi": "\uFD8F", + "/meem.init_meem.fina": "\uFC48", + "/meem.init_meem.medi": "\uFCD1", + "/meem.init_yeh.fina": "\uFC4A", + "/meem.isol": "\uFEE1", + "/meem.medi": "\uFEE4", + "/meem.medi_alef.fina": "\uFC88", + "/meem.medi_hah.medi_yeh.fina": "\uFD8B", + "/meem.medi_jeem.medi_yeh.fina": "\uFDC0", + "/meem.medi_khah.medi_yeh.fina": "\uFDB9", + "/meem.medi_meem.fina": "\uFC89", + "/meem.medi_meem.medi_yeh.fina": "\uFDB1", + "/meemDotAbove": "\u0765", + "/meemDotBelow": "\u0766", + "/meemabove": "\u06E2", + "/meemabove.init": "\u06D8", + "/meemarabic": "\u0645", + "/meembelow": "\u06ED", + "/meemfinalarabic": "\uFEE2", + "/meeminitialarabic": "\uFEE3", + "/meemmedialarabic": "\uFEE4", + "/meemmeeminitialarabic": "\uFCD1", + "/meemmeemisolatedarabic": "\uFC48", + "/meetorusquare": "\u334D", + "/megasquare": "\u334B", + "/megatonsquare": "\u334C", + "/mehiragana": "\u3081", + "/meizierasquare": "\u337E", + "/mekatakana": "\u30E1", + "/mekatakanahalfwidth": "\uFF92", + "/melon": "\u1F348", + "/mem": "\u05DE", + "/mem:hb": "\u05DE", + "/memdagesh": "\uFB3E", + "/memdageshhebrew": "\uFB3E", + "/memhebrew": "\u05DE", + "/memo": "\u1F4DD", + "/memwithdagesh:hb": "\uFB3E", + "/menarmenian": "\u0574", + "/menorahNineBranches": "\u1F54E", + "/menpostSindhi": "\u06FE", + "/mens": "\u1F6B9", + "/mepigraphicinverted": "\uA7FD", + "/mercha:hb": "\u05A5", + "/merchaKefulah:hb": "\u05A6", + "/mercury": "\u263F", + "/merkhahebrew": "\u05A5", + "/merkhakefulahebrew": "\u05A6", + "/merkhakefulalefthebrew": "\u05A6", + "/merkhalefthebrew": "\u05A5", + "/metalideographiccircled": "\u328E", + "/metalideographicparen": "\u322E", + "/meteg:hb": "\u05BD", + "/metro": "\u1F687", + "/mgfullwidth": "\u338E", + "/mhook": "\u0271", + "/mhzfullwidth": "\u3392", + "/mhzsquare": "\u3392", + "/micirclekatakana": "\u32EF", + "/microphone": "\u1F3A4", + "/microscope": "\u1F52C", + "/middledotkatakanahalfwidth": "\uFF65", + "/middot": "\u00B7", + "/mieumacirclekorean": "\u3272", + "/mieumaparenkorean": "\u3212", + "/mieumcirclekorean": "\u3264", + "/mieumkorean": "\u3141", + "/mieumpansioskorean": "\u3170", + "/mieumparenkorean": "\u3204", + "/mieumpieupkorean": "\u316E", + "/mieumsioskorean": "\u316F", + "/mihiragana": "\u307F", + "/mikatakana": "\u30DF", + "/mikatakanahalfwidth": "\uFF90", + "/mikuronsquare": "\u3348", + "/milfullwidth": "\u33D5", + "/militaryMedal": "\u1F396", + "/milkyWay": "\u1F30C", + "/mill": "\u20A5", + "/millionscmbcyr": "\u0489", + "/millisecond": "\u2034", + "/millisecondreversed": "\u2037", + "/minibus": "\u1F690", + "/minidisc": "\u1F4BD", + "/minimize": "\u1F5D5", + "/minus": "\u2212", + "/minus.inferior": "\u208B", + "/minus.superior": "\u207B", + "/minusbelowcmb": "\u0320", + "/minuscircle": "\u2296", + "/minusmod": "\u02D7", + "/minusplus": "\u2213", + "/minussignmod": "\u02D7", + "/minustilde": "\u2242", + "/minute": "\u2032", + "/minutereversed": "\u2035", + "/miribaarusquare": "\u334A", + "/mirisquare": "\u3349", + "/misc:baby": "\u1F476", + "/misc:bell": "\u1F514", + "/misc:dash": "\u1F4A8", + "/misc:decimalseparator": "\u2396", + "/misc:diamondblack": "\u2666", + "/misc:diamondwhite": "\u2662", + "/misc:ear": "\u1F442", + "/misc:om": "\u1F549", + "/misc:ring": "\u1F48D", + "/misra": "\u060F", + "/mlfullwidth": "\u3396", + "/mlonglegturned": "\u0270", + "/mlsquare": "\u3396", + "/mlym:a": "\u0D05", + "/mlym:aa": "\u0D06", + "/mlym:aasign": "\u0D3E", + "/mlym:ai": "\u0D10", + "/mlym:aisign": "\u0D48", + "/mlym:anusvarasign": "\u0D02", + "/mlym:archaicii": "\u0D5F", + "/mlym:au": "\u0D14", + "/mlym:aulength": "\u0D57", + "/mlym:ausign": "\u0D4C", + "/mlym:avagrahasign": "\u0D3D", + "/mlym:ba": "\u0D2C", + "/mlym:bha": "\u0D2D", + "/mlym:ca": "\u0D1A", + "/mlym:candrabindusign": "\u0D01", + "/mlym:cha": "\u0D1B", + "/mlym:circularviramasign": "\u0D3C", + "/mlym:combininganusvaraabovesign": "\u0D00", + "/mlym:da": "\u0D26", + "/mlym:date": "\u0D79", + "/mlym:dda": "\u0D21", + "/mlym:ddha": "\u0D22", + "/mlym:dha": "\u0D27", + "/mlym:dotreph": "\u0D4E", + "/mlym:e": "\u0D0E", + "/mlym:ee": "\u0D0F", + "/mlym:eesign": "\u0D47", + "/mlym:eight": "\u0D6E", + "/mlym:esign": "\u0D46", + "/mlym:five": "\u0D6B", + "/mlym:four": "\u0D6A", + "/mlym:ga": "\u0D17", + "/mlym:gha": "\u0D18", + "/mlym:ha": "\u0D39", + "/mlym:i": "\u0D07", + "/mlym:ii": "\u0D08", + "/mlym:iisign": "\u0D40", + "/mlym:isign": "\u0D3F", + "/mlym:ja": "\u0D1C", + "/mlym:jha": "\u0D1D", + "/mlym:ka": "\u0D15", + "/mlym:kchillu": "\u0D7F", + "/mlym:kha": "\u0D16", + "/mlym:la": "\u0D32", + "/mlym:lchillu": "\u0D7D", + "/mlym:lla": "\u0D33", + "/mlym:llchillu": "\u0D7E", + "/mlym:llla": "\u0D34", + "/mlym:lllchillu": "\u0D56", + "/mlym:llvocal": "\u0D61", + "/mlym:llvocalsign": "\u0D63", + "/mlym:lvocal": "\u0D0C", + "/mlym:lvocalsign": "\u0D62", + "/mlym:ma": "\u0D2E", + "/mlym:mchillu": "\u0D54", + "/mlym:na": "\u0D28", + "/mlym:nchillu": "\u0D7B", + "/mlym:nga": "\u0D19", + "/mlym:nine": "\u0D6F", + "/mlym:nna": "\u0D23", + "/mlym:nnchillu": "\u0D7A", + "/mlym:nnna": "\u0D29", + "/mlym:nya": "\u0D1E", + "/mlym:o": "\u0D12", + "/mlym:one": "\u0D67", + "/mlym:oneeighth": "\u0D77", + "/mlym:onefifth": "\u0D5E", + "/mlym:onefortieth": "\u0D59", + "/mlym:onehalf": "\u0D74", + "/mlym:onehundred": "\u0D71", + "/mlym:oneone-hundred-and-sixtieth": "\u0D58", + "/mlym:onequarter": "\u0D73", + "/mlym:onesixteenth": "\u0D76", + "/mlym:onetenth": "\u0D5C", + "/mlym:onethousand": "\u0D72", + "/mlym:onetwentieth": "\u0D5B", + "/mlym:oo": "\u0D13", + "/mlym:oosign": "\u0D4B", + "/mlym:osign": "\u0D4A", + "/mlym:pa": "\u0D2A", + "/mlym:parasign": "\u0D4F", + "/mlym:pha": "\u0D2B", + "/mlym:ra": "\u0D30", + "/mlym:rra": "\u0D31", + "/mlym:rrchillu": "\u0D7C", + "/mlym:rrvocal": "\u0D60", + "/mlym:rrvocalsign": "\u0D44", + "/mlym:rvocal": "\u0D0B", + "/mlym:rvocalsign": "\u0D43", + "/mlym:sa": "\u0D38", + "/mlym:seven": "\u0D6D", + "/mlym:sha": "\u0D36", + "/mlym:six": "\u0D6C", + "/mlym:ssa": "\u0D37", + "/mlym:ta": "\u0D24", + "/mlym:ten": "\u0D70", + "/mlym:tha": "\u0D25", + "/mlym:three": "\u0D69", + "/mlym:threeeightieths": "\u0D5A", + "/mlym:threequarters": "\u0D75", + "/mlym:threesixteenths": "\u0D78", + "/mlym:threetwentieths": "\u0D5D", + "/mlym:tta": "\u0D1F", + "/mlym:ttha": "\u0D20", + "/mlym:ttta": "\u0D3A", + "/mlym:two": "\u0D68", + "/mlym:u": "\u0D09", + "/mlym:usign": "\u0D41", + "/mlym:uu": "\u0D0A", + "/mlym:uusign": "\u0D42", + "/mlym:va": "\u0D35", + "/mlym:verticalbarviramasign": "\u0D3B", + "/mlym:viramasign": "\u0D4D", + "/mlym:visargasign": "\u0D03", + "/mlym:ya": "\u0D2F", + "/mlym:ychillu": "\u0D55", + "/mlym:zero": "\u0D66", + "/mm2fullwidth": "\u339F", + "/mm3fullwidth": "\u33A3", + "/mmcubedsquare": "\u33A3", + "/mmfullwidth": "\u339C", + "/mmonospace": "\uFF4D", + "/mmsquaredsquare": "\u339F", + "/mobilePhone": "\u1F4F1", + "/mobilePhoneOff": "\u1F4F4", + "/mobilePhoneRightwardsArrowAtLeft": "\u1F4F2", + "/mocirclekatakana": "\u32F2", + "/models": "\u22A7", + "/mohiragana": "\u3082", + "/mohmfullwidth": "\u33C1", + "/mohmsquare": "\u33C1", + "/mokatakana": "\u30E2", + "/mokatakanahalfwidth": "\uFF93", + "/molfullwidth": "\u33D6", + "/molsquare": "\u33D6", + "/momathai": "\u0E21", + "/moneyBag": "\u1F4B0", + "/moneyWings": "\u1F4B8", + "/mong:a": "\u1820", + "/mong:aaligali": "\u1887", + "/mong:ahaligali": "\u1897", + "/mong:ang": "\u1829", + "/mong:angsibe": "\u1862", + "/mong:angtodo": "\u184A", + "/mong:anusvaraonealigali": "\u1880", + "/mong:ba": "\u182A", + "/mong:baludaaligali": "\u1885", + "/mong:baludaaligalithree": "\u1886", + "/mong:batodo": "\u184B", + "/mong:bhamanchualigali": "\u18A8", + "/mong:birga": "\u1800", + "/mong:caaligali": "\u188B", + "/mong:camanchualigali": "\u189C", + "/mong:cha": "\u1834", + "/mong:chasibe": "\u1871", + "/mong:chatodo": "\u1852", + "/mong:chi": "\u1842", + "/mong:colon": "\u1804", + "/mong:comma": "\u1802", + "/mong:commamanchu": "\u1808", + "/mong:cyamanchualigali": "\u18A3", + "/mong:da": "\u1833", + "/mong:daaligali": "\u1891", + "/mong:dagalgaaligali": "\u18A9", + "/mong:damarualigali": "\u1882", + "/mong:dasibe": "\u1869", + "/mong:datodo": "\u1851", + "/mong:ddaaligali": "\u188E", + "/mong:ddhamanchualigali": "\u189F", + "/mong:dhamanchualigali": "\u18A1", + "/mong:dzatodo": "\u185C", + "/mong:e": "\u1821", + "/mong:ee": "\u1827", + "/mong:eight": "\u1818", + "/mong:ellipsis": "\u1801", + "/mong:esibe": "\u185D", + "/mong:etodo": "\u1844", + "/mong:fa": "\u1839", + "/mong:famanchu": "\u1876", + "/mong:fasibe": "\u186B", + "/mong:five": "\u1815", + "/mong:four": "\u1814", + "/mong:fourdots": "\u1805", + "/mong:freevariationselectorone": "\u180B", + "/mong:freevariationselectorthree": "\u180D", + "/mong:freevariationselectortwo": "\u180C", + "/mong:ga": "\u182D", + "/mong:gaasibe": "\u186C", + "/mong:gaatodo": "\u1858", + "/mong:gasibe": "\u1864", + "/mong:gatodo": "\u184E", + "/mong:ghamanchualigali": "\u189A", + "/mong:haa": "\u183E", + "/mong:haasibe": "\u186D", + "/mong:haatodo": "\u1859", + "/mong:hasibe": "\u1865", + "/mong:i": "\u1822", + "/mong:ialigali": "\u1888", + "/mong:imanchu": "\u1873", + "/mong:isibe": "\u185E", + "/mong:itodo": "\u1845", + "/mong:iysibe": "\u185F", + "/mong:ja": "\u1835", + "/mong:jasibe": "\u186A", + "/mong:jatodo": "\u1853", + "/mong:jhamanchualigali": "\u189D", + "/mong:jiatodo": "\u185A", + "/mong:ka": "\u183A", + "/mong:kaaligali": "\u1889", + "/mong:kamanchu": "\u1874", + "/mong:kasibe": "\u1863", + "/mong:katodo": "\u1857", + "/mong:kha": "\u183B", + "/mong:la": "\u182F", + "/mong:lha": "\u1840", + "/mong:lhamanchualigali": "\u18AA", + "/mong:longvowelsigntodo": "\u1843", + "/mong:ma": "\u182E", + "/mong:matodo": "\u184F", + "/mong:na": "\u1828", + "/mong:ngaaligali": "\u188A", + "/mong:ngamanchualigali": "\u189B", + "/mong:niatodo": "\u185B", + "/mong:nine": "\u1819", + "/mong:nirugu": "\u180A", + "/mong:nnaaligali": "\u188F", + "/mong:o": "\u1823", + "/mong:oe": "\u1825", + "/mong:oetodo": "\u1848", + "/mong:one": "\u1811", + "/mong:otodo": "\u1846", + "/mong:pa": "\u182B", + "/mong:paaligali": "\u1892", + "/mong:pasibe": "\u1866", + "/mong:patodo": "\u184C", + "/mong:period": "\u1803", + "/mong:periodmanchu": "\u1809", + "/mong:phaaligali": "\u1893", + "/mong:qa": "\u182C", + "/mong:qatodo": "\u184D", + "/mong:ra": "\u1837", + "/mong:raasibe": "\u1870", + "/mong:ramanchu": "\u1875", + "/mong:sa": "\u1830", + "/mong:seven": "\u1817", + "/mong:sha": "\u1831", + "/mong:shasibe": "\u1867", + "/mong:six": "\u1816", + "/mong:softhyphentodo": "\u1806", + "/mong:ssaaligali": "\u1894", + "/mong:ssamanchualigali": "\u18A2", + "/mong:syllableboundarymarkersibe": "\u1807", + "/mong:ta": "\u1832", + "/mong:taaligali": "\u1890", + "/mong:tamanchualigali": "\u18A0", + "/mong:tasibe": "\u1868", + "/mong:tatodo": "\u1850", + "/mong:tatodoaligali": "\u1898", + "/mong:three": "\u1813", + "/mong:tsa": "\u183C", + "/mong:tsasibe": "\u186E", + "/mong:tsatodo": "\u1854", + "/mong:ttaaligali": "\u188C", + "/mong:ttamanchualigali": "\u189E", + "/mong:tthaaligali": "\u188D", + "/mong:two": "\u1812", + "/mong:u": "\u1824", + "/mong:ualigalihalf": "\u18A6", + "/mong:ubadamaaligali": "\u1883", + "/mong:ubadamaaligaliinverted": "\u1884", + "/mong:ue": "\u1826", + "/mong:uesibe": "\u1860", + "/mong:uetodo": "\u1849", + "/mong:usibe": "\u1861", + "/mong:utodo": "\u1847", + "/mong:visargaonealigali": "\u1881", + "/mong:vowelseparator": "\u180E", + "/mong:wa": "\u1838", + "/mong:watodo": "\u1856", + "/mong:ya": "\u1836", + "/mong:yaaligalihalf": "\u18A7", + "/mong:yatodo": "\u1855", + "/mong:za": "\u183D", + "/mong:zaaligali": "\u1896", + "/mong:zamanchualigali": "\u18A5", + "/mong:zasibe": "\u186F", + "/mong:zero": "\u1810", + "/mong:zhaaligali": "\u1895", + "/mong:zhamanchu": "\u1877", + "/mong:zhamanchualigali": "\u18A4", + "/mong:zhasibe": "\u1872", + "/mong:zhatodoaligali": "\u1899", + "/mong:zhi": "\u1841", + "/mong:zra": "\u183F", + "/monkey": "\u1F412", + "/monkeyFace": "\u1F435", + "/monogramyang": "\u268A", + "/monogramyin": "\u268B", + "/monorail": "\u1F69D", + "/monostable": "\u238D", + "/moodBubble": "\u1F5F0", + "/moonViewingCeremony": "\u1F391", + "/moonideographiccircled": "\u328A", + "/moonideographicparen": "\u322A", + "/moonlilithblack": "\u26B8", + "/mosque": "\u1F54C", + "/motorBoat": "\u1F6E5", + "/motorScooter": "\u1F6F5", + "/motorway": "\u1F6E3", + "/mountFuji": "\u1F5FB", + "/mountain": "\u26F0", + "/mountainBicyclist": "\u1F6B5", + "/mountainCableway": "\u1F6A0", + "/mountainRailway": "\u1F69E", + "/mouse": "\u1F401", + "/mouseFace": "\u1F42D", + "/mouth": "\u1F444", + "/movers2fullwidth": "\u33A8", + "/moversfullwidth": "\u33A7", + "/moverssquare": "\u33A7", + "/moverssquaredsquare": "\u33A8", + "/movieCamera": "\u1F3A5", + "/moyai": "\u1F5FF", + "/mpafullwidth": "\u33AB", + "/mparen": "\u24A8", + "/mparenthesized": "\u24A8", + "/mpasquare": "\u33AB", + "/msfullwidth": "\u33B3", + "/mssquare": "\u33B3", + "/msuperior": "\uF6EF", + "/mturned": "\u026F", + "/mu": "\u00B5", + "/mu.math": "\u00B5", + "/mu1": "\u00B5", + "/muafullwidth": "\u3382", + "/muasquare": "\u3382", + "/muchgreater": "\u226B", + "/muchless": "\u226A", + "/mucirclekatakana": "\u32F0", + "/muffullwidth": "\u338C", + "/mufsquare": "\u338C", + "/mugfullwidth": "\u338D", + "/mugreek": "\u03BC", + "/mugsquare": "\u338D", + "/muhiragana": "\u3080", + "/mukatakana": "\u30E0", + "/mukatakanahalfwidth": "\uFF91", + "/mulfullwidth": "\u3395", + "/mulsquare": "\u3395", + "/multimap": "\u22B8", + "/multimapleft": "\u27DC", + "/multipleMusicalNotes": "\u1F3B6", + "/multiply": "\u00D7", + "/multiset": "\u228C", + "/multisetmultiplication": "\u228D", + "/multisetunion": "\u228E", + "/mum": "\uA773", + "/mumfullwidth": "\u339B", + "/mumsquare": "\u339B", + "/munach:hb": "\u05A3", + "/munahhebrew": "\u05A3", + "/munahlefthebrew": "\u05A3", + "/musfullwidth": "\u33B2", + "/mushroom": "\u1F344", + "/musicalKeyboard": "\u1F3B9", + "/musicalKeyboardJacks": "\u1F398", + "/musicalNote": "\u1F3B5", + "/musicalScore": "\u1F3BC", + "/musicalnote": "\u266A", + "/musicalnotedbl": "\u266B", + "/musicflat": "\u266D", + "/musicflatsign": "\u266D", + "/musicnatural": "\u266E", + "/musicsharp": "\u266F", + "/musicsharpsign": "\u266F", + "/mussquare": "\u33B2", + "/muvfullwidth": "\u33B6", + "/muvsquare": "\u33B6", + "/muwfullwidth": "\u33BC", + "/muwsquare": "\u33BC", + "/mvfullwidth": "\u33B7", + "/mvmegafullwidth": "\u33B9", + "/mvmegasquare": "\u33B9", + "/mvsquare": "\u33B7", + "/mwfullwidth": "\u33BD", + "/mwmegafullwidth": "\u33BF", + "/mwmegasquare": "\u33BF", + "/mwsquare": "\u33BD", + "/n": "\u006E", + "/n.inferior": "\u2099", + "/n.superior": "\u207F", + "/nabengali": "\u09A8", + "/nabla": "\u2207", + "/nacirclekatakana": "\u32E4", + "/nacute": "\u0144", + "/nadeva": "\u0928", + "/nafullwidth": "\u3381", + "/nagujarati": "\u0AA8", + "/nagurmukhi": "\u0A28", + "/nahiragana": "\u306A", + "/nailPolish": "\u1F485", + "/naira": "\u20A6", + "/nakatakana": "\u30CA", + "/nakatakanahalfwidth": "\uFF85", + "/nameBadge": "\u1F4DB", + "/nameideographiccircled": "\u3294", + "/nameideographicparen": "\u3234", + "/namurda": "\uA99F", + "/nand": "\u22BC", + "/nanosquare": "\u3328", + "/napostrophe": "\u0149", + "/narrownobreakspace": "\u202F", + "/nasquare": "\u3381", + "/nationalPark": "\u1F3DE", + "/nationaldigitshapes": "\u206E", + "/nbopomofo": "\u310B", + "/nbspace": "\u00A0", + "/ncaron": "\u0148", + "/ncedilla": "\u0146", + "/ncircle": "\u24DD", + "/ncircumflexbelow": "\u1E4B", + "/ncommaaccent": "\u0146", + "/ncurl": "\u0235", + "/ndescender": "\uA791", + "/ndot": "\u1E45", + "/ndotaccent": "\u1E45", + "/ndotbelow": "\u1E47", + "/necirclekatakana": "\u32E7", + "/necktie": "\u1F454", + "/negatedturnstiledblverticalbarright": "\u22AF", + "/nehiragana": "\u306D", + "/neirapproximatelynoractuallyequal": "\u2247", + "/neirasersetnorequalup": "\u2289", + "/neirasubsetnorequal": "\u2288", + "/neirgreaternorequal": "\u2271", + "/neirgreaternorequivalent": "\u2275", + "/neirgreaternorless": "\u2279", + "/neirlessnorequal": "\u2270", + "/neirlessnorequivalent": "\u2274", + "/neirlessnorgreater": "\u2278", + "/nekatakana": "\u30CD", + "/nekatakanahalfwidth": "\uFF88", + "/neptune": "\u2646", + "/neuter": "\u26B2", + "/neutralFace": "\u1F610", + "/newMoon": "\u1F311", + "/newMoonFace": "\u1F31A", + "/newsheqel": "\u20AA", + "/newsheqelsign": "\u20AA", + "/newspaper": "\u1F4F0", + "/newsquare": "\u1F195", + "/nextpage": "\u2398", + "/nffullwidth": "\u338B", + "/nfsquare": "\u338B", + "/ng.fina": "\uFBD4", + "/ng.init": "\uFBD5", + "/ng.isol": "\uFBD3", + "/ng.medi": "\uFBD6", + "/ngabengali": "\u0999", + "/ngadeva": "\u0919", + "/ngagujarati": "\u0A99", + "/ngagurmukhi": "\u0A19", + "/ngalelet": "\uA98A", + "/ngaleletraswadi": "\uA98B", + "/ngoeh": "\u06B1", + "/ngoeh.fina": "\uFB9B", + "/ngoeh.init": "\uFB9C", + "/ngoeh.isol": "\uFB9A", + "/ngoeh.medi": "\uFB9D", + "/ngonguthai": "\u0E07", + "/ngrave": "\u01F9", + "/ngsquare": "\u1F196", + "/nhiragana": "\u3093", + "/nhookleft": "\u0272", + "/nhookretroflex": "\u0273", + "/nicirclekatakana": "\u32E5", + "/nieunacirclekorean": "\u326F", + "/nieunaparenkorean": "\u320F", + "/nieuncieuckorean": "\u3135", + "/nieuncirclekorean": "\u3261", + "/nieunhieuhkorean": "\u3136", + "/nieunkorean": "\u3134", + "/nieunpansioskorean": "\u3168", + "/nieunparenkorean": "\u3201", + "/nieunsioskorean": "\u3167", + "/nieuntikeutkorean": "\u3166", + "/nightStars": "\u1F303", + "/nightideographiccircled": "\u32B0", + "/nihiragana": "\u306B", + "/nikatakana": "\u30CB", + "/nikatakanahalfwidth": "\uFF86", + "/nikhahitleftthai": "\uF899", + "/nikhahitthai": "\u0E4D", + "/nine": "\u0039", + "/nine.inferior": "\u2089", + "/nine.roman": "\u2168", + "/nine.romansmall": "\u2178", + "/nine.superior": "\u2079", + "/ninearabic": "\u0669", + "/ninebengali": "\u09EF", + "/ninecircle": "\u2468", + "/ninecircledbl": "\u24FD", + "/ninecircleinversesansserif": "\u2792", + "/ninecomma": "\u1F10A", + "/ninedeva": "\u096F", + "/ninefar": "\u06F9", + "/ninegujarati": "\u0AEF", + "/ninegurmukhi": "\u0A6F", + "/ninehackarabic": "\u0669", + "/ninehangzhou": "\u3029", + "/nineideographiccircled": "\u3288", + "/nineideographicparen": "\u3228", + "/nineinferior": "\u2089", + "/ninemonospace": "\uFF19", + "/nineoldstyle": "\uF739", + "/nineparen": "\u247C", + "/nineparenthesized": "\u247C", + "/nineperiod": "\u2490", + "/ninepersian": "\u06F9", + "/nineroman": "\u2178", + "/ninesuperior": "\u2079", + "/nineteencircle": "\u2472", + "/nineteencircleblack": "\u24F3", + "/nineteenparen": "\u2486", + "/nineteenparenthesized": "\u2486", + "/nineteenperiod": "\u249A", + "/ninethai": "\u0E59", + "/nj": "\u01CC", + "/njecyr": "\u045A", + "/njecyrillic": "\u045A", + "/njekomicyr": "\u050B", + "/nkatakana": "\u30F3", + "/nkatakanahalfwidth": "\uFF9D", + "/nlegrightlong": "\u019E", + "/nlinebelow": "\u1E49", + "/nlongrightleg": "\u019E", + "/nmbr:oneeighth": "\u215B", + "/nmbr:onefifth": "\u2155", + "/nmbr:onetenth": "\u2152", + "/nmfullwidth": "\u339A", + "/nmonospace": "\uFF4E", + "/nmsquare": "\u339A", + "/nnabengali": "\u09A3", + "/nnadeva": "\u0923", + "/nnagujarati": "\u0AA3", + "/nnagurmukhi": "\u0A23", + "/nnnadeva": "\u0929", + "/noBicycles": "\u1F6B3", + "/noEntrySign": "\u1F6AB", + "/noMobilePhones": "\u1F4F5", + "/noOneUnderEighteen": "\u1F51E", + "/noPedestrians": "\u1F6B7", + "/noPiracy": "\u1F572", + "/noSmoking": "\u1F6AD", + "/nobliquestroke": "\uA7A5", + "/nocirclekatakana": "\u32E8", + "/nodeascending": "\u260A", + "/nodedescending": "\u260B", + "/noentry": "\u26D4", + "/nohiragana": "\u306E", + "/nokatakana": "\u30CE", + "/nokatakanahalfwidth": "\uFF89", + "/nominaldigitshapes": "\u206F", + "/nonPotableWater": "\u1F6B1", + "/nonbreakinghyphen": "\u2011", + "/nonbreakingspace": "\u00A0", + "/nonenthai": "\u0E13", + "/nonuthai": "\u0E19", + "/noon": "\u0646", + "/noon.fina": "\uFEE6", + "/noon.init": "\uFEE7", + "/noon.init_alefmaksura.fina": "\uFC4F", + "/noon.init_hah.fina": "\uFC4C", + "/noon.init_hah.medi": "\uFCD3", + "/noon.init_hah.medi_meem.medi": "\uFD95", + "/noon.init_heh.medi": "\uFCD6", + "/noon.init_jeem.fina": "\uFC4B", + "/noon.init_jeem.medi": "\uFCD2", + "/noon.init_jeem.medi_hah.medi": "\uFDB8", + "/noon.init_jeem.medi_meem.medi": "\uFD98", + "/noon.init_khah.fina": "\uFC4D", + "/noon.init_khah.medi": "\uFCD4", + "/noon.init_meem.fina": "\uFC4E", + "/noon.init_meem.medi": "\uFCD5", + "/noon.init_yeh.fina": "\uFC50", + "/noon.isol": "\uFEE5", + "/noon.medi": "\uFEE8", + "/noon.medi_alefmaksura.fina": "\uFC8E", + "/noon.medi_hah.medi_alefmaksura.fina": "\uFD96", + "/noon.medi_hah.medi_yeh.fina": "\uFDB3", + "/noon.medi_heh.medi": "\uFCEF", + "/noon.medi_jeem.medi_alefmaksura.fina": "\uFD99", + "/noon.medi_jeem.medi_hah.fina": "\uFDBD", + "/noon.medi_jeem.medi_meem.fina": "\uFD97", + "/noon.medi_jeem.medi_yeh.fina": "\uFDC7", + "/noon.medi_meem.fina": "\uFC8C", + "/noon.medi_meem.medi": "\uFCEE", + "/noon.medi_meem.medi_alefmaksura.fina": "\uFD9B", + "/noon.medi_meem.medi_yeh.fina": "\uFD9A", + "/noon.medi_noon.fina": "\uFC8D", + "/noon.medi_reh.fina": "\uFC8A", + "/noon.medi_yeh.fina": "\uFC8F", + "/noon.medi_zain.fina": "\uFC8B", + "/noonSmallTah": "\u0768", + "/noonSmallV": "\u0769", + "/noonTwoDotsBelow": "\u0767", + "/noonabove": "\u06E8", + "/noonarabic": "\u0646", + "/noondotbelow": "\u06B9", + "/noonfinalarabic": "\uFEE6", + "/noonghunna": "\u06BA", + "/noonghunna.fina": "\uFB9F", + "/noonghunna.isol": "\uFB9E", + "/noonghunnaarabic": "\u06BA", + "/noonghunnafinalarabic": "\uFB9F", + "/noonhehinitialarabic": "\uFEE7", + "/nooninitialarabic": "\uFEE7", + "/noonjeeminitialarabic": "\uFCD2", + "/noonjeemisolatedarabic": "\uFC4B", + "/noonmedialarabic": "\uFEE8", + "/noonmeeminitialarabic": "\uFCD5", + "/noonmeemisolatedarabic": "\uFC4E", + "/noonnoonfinalarabic": "\uFC8D", + "/noonring": "\u06BC", + "/noonthreedotsabove": "\u06BD", + "/nor": "\u22BD", + "/nordicmark": "\u20BB", + "/normalfacrsemidirectproductleft": "\u22C9", + "/normalfacrsemidirectproductright": "\u22CA", + "/normalsubgroorequalup": "\u22B4", + "/normalsubgroup": "\u22B2", + "/northeastPointingAirplane": "\u1F6EA", + "/nose": "\u1F443", + "/notalmostequal": "\u2249", + "/notasersetup": "\u2285", + "/notasympticallyequal": "\u2244", + "/notcheckmark": "\u237B", + "/notchedLeftSemicircleThreeDots": "\u1F543", + "/notchedRightSemicircleThreeDots": "\u1F544", + "/notcontains": "\u220C", + "/note": "\u1F5C8", + "/notePad": "\u1F5CA", + "/notePage": "\u1F5C9", + "/notebook": "\u1F4D3", + "/notebookDecorativeCover": "\u1F4D4", + "/notelement": "\u2209", + "/notelementof": "\u2209", + "/notequal": "\u2260", + "/notequivalent": "\u226D", + "/notexistential": "\u2204", + "/notgreater": "\u226F", + "/notgreaternorequal": "\u2271", + "/notgreaternorless": "\u2279", + "/notidentical": "\u2262", + "/notless": "\u226E", + "/notlessnorequal": "\u2270", + "/notnormalsubgroorequalup": "\u22EC", + "/notnormalsubgroup": "\u22EA", + "/notparallel": "\u2226", + "/notprecedes": "\u2280", + "/notsignturned": "\u2319", + "/notsquareimageorequal": "\u22E2", + "/notsquareoriginalorequal": "\u22E3", + "/notsubset": "\u2284", + "/notsucceeds": "\u2281", + "/notsuperset": "\u2285", + "/nottilde": "\u2241", + "/nottosquare": "\u3329", + "/nottrue": "\u22AD", + "/novembertelegraph": "\u32CA", + "/nowarmenian": "\u0576", + "/nparen": "\u24A9", + "/nparenthesized": "\u24A9", + "/nretroflex": "\u0273", + "/nsfullwidth": "\u33B1", + "/nssquare": "\u33B1", + "/nsuperior": "\u207F", + "/ntilde": "\u00F1", + "/nu": "\u03BD", + "/nucirclekatakana": "\u32E6", + "/nuhiragana": "\u306C", + "/nukatakana": "\u30CC", + "/nukatakanahalfwidth": "\uFF87", + "/nuktabengali": "\u09BC", + "/nuktadeva": "\u093C", + "/nuktagujarati": "\u0ABC", + "/nuktagurmukhi": "\u0A3C", + "/num": "\uA774", + "/numbermarkabove": "\u0605", + "/numbersign": "\u0023", + "/numbersignmonospace": "\uFF03", + "/numbersignsmall": "\uFE5F", + "/numeralsign": "\u0374", + "/numeralsigngreek": "\u0374", + "/numeralsignlowergreek": "\u0375", + "/numero": "\u2116", + "/nun": "\u05E0", + "/nun:hb": "\u05E0", + "/nunHafukha:hb": "\u05C6", + "/nundagesh": "\uFB40", + "/nundageshhebrew": "\uFB40", + "/nunhebrew": "\u05E0", + "/nunwithdagesh:hb": "\uFB40", + "/nutAndBolt": "\u1F529", + "/nvfullwidth": "\u33B5", + "/nvsquare": "\u33B5", + "/nwfullwidth": "\u33BB", + "/nwsquare": "\u33BB", + "/nyabengali": "\u099E", + "/nyadeva": "\u091E", + "/nyagujarati": "\u0A9E", + "/nyagurmukhi": "\u0A1E", + "/nyamurda": "\uA998", + "/nyeh": "\u0683", + "/nyeh.fina": "\uFB77", + "/nyeh.init": "\uFB78", + "/nyeh.isol": "\uFB76", + "/nyeh.medi": "\uFB79", + "/o": "\u006F", + "/o.inferior": "\u2092", + "/oacute": "\u00F3", + "/oangthai": "\u0E2D", + "/obarcyr": "\u04E9", + "/obardieresiscyr": "\u04EB", + "/obarred": "\u0275", + "/obarredcyrillic": "\u04E9", + "/obarreddieresiscyrillic": "\u04EB", + "/obelosdotted": "\u2E13", + "/obengali": "\u0993", + "/obopomofo": "\u311B", + "/obreve": "\u014F", + "/observereye": "\u23FF", + "/ocandradeva": "\u0911", + "/ocandragujarati": "\u0A91", + "/ocandravowelsigndeva": "\u0949", + "/ocandravowelsigngujarati": "\u0AC9", + "/ocaron": "\u01D2", + "/ocircle": "\u24DE", + "/ocirclekatakana": "\u32D4", + "/ocircumflex": "\u00F4", + "/ocircumflexacute": "\u1ED1", + "/ocircumflexdotbelow": "\u1ED9", + "/ocircumflexgrave": "\u1ED3", + "/ocircumflexhoi": "\u1ED5", + "/ocircumflexhookabove": "\u1ED5", + "/ocircumflextilde": "\u1ED7", + "/ocr:bowtie": "\u2445", + "/ocr:dash": "\u2448", + "/octagonalSign": "\u1F6D1", + "/octobertelegraph": "\u32C9", + "/octopus": "\u1F419", + "/ocyr": "\u043E", + "/ocyrillic": "\u043E", + "/odblacute": "\u0151", + "/odblgrave": "\u020D", + "/oden": "\u1F362", + "/odeva": "\u0913", + "/odieresis": "\u00F6", + "/odieresiscyr": "\u04E7", + "/odieresiscyrillic": "\u04E7", + "/odieresismacron": "\u022B", + "/odot": "\u022F", + "/odotbelow": "\u1ECD", + "/odotmacron": "\u0231", + "/oe": "\u0153", + "/oe.fina": "\uFBDA", + "/oe.isol": "\uFBD9", + "/oekirghiz": "\u06C5", + "/oekirghiz.fina": "\uFBE1", + "/oekirghiz.isol": "\uFBE0", + "/oekorean": "\u315A", + "/officeBuilding": "\u1F3E2", + "/ogonek": "\u02DB", + "/ogonekcmb": "\u0328", + "/ograve": "\u00F2", + "/ogravedbl": "\u020D", + "/ogujarati": "\u0A93", + "/oharmenian": "\u0585", + "/ohiragana": "\u304A", + "/ohm": "\u2126", + "/ohminverted": "\u2127", + "/ohoi": "\u1ECF", + "/ohookabove": "\u1ECF", + "/ohorn": "\u01A1", + "/ohornacute": "\u1EDB", + "/ohorndotbelow": "\u1EE3", + "/ohorngrave": "\u1EDD", + "/ohornhoi": "\u1EDF", + "/ohornhookabove": "\u1EDF", + "/ohorntilde": "\u1EE1", + "/ohungarumlaut": "\u0151", + "/ohuparen": "\u321E", + "/oi": "\u01A3", + "/oilDrum": "\u1F6E2", + "/oinvertedbreve": "\u020F", + "/ojeonparen": "\u321D", + "/okHandSign": "\u1F44C", + "/okatakana": "\u30AA", + "/okatakanahalfwidth": "\uFF75", + "/okorean": "\u3157", + "/oksquare": "\u1F197", + "/oldKey": "\u1F5DD", + "/oldPersonalComputer": "\u1F5B3", + "/olderMan": "\u1F474", + "/olderWoman": "\u1F475", + "/ole:hb": "\u05AB", + "/olehebrew": "\u05AB", + "/oloop": "\uA74D", + "/olowringinside": "\u2C7A", + "/omacron": "\u014D", + "/omacronacute": "\u1E53", + "/omacrongrave": "\u1E51", + "/omdeva": "\u0950", + "/omega": "\u03C9", + "/omega1": "\u03D6", + "/omegaacute": "\u1F7D", + "/omegaacuteiotasub": "\u1FF4", + "/omegaasper": "\u1F61", + "/omegaasperacute": "\u1F65", + "/omegaasperacuteiotasub": "\u1FA5", + "/omegaaspergrave": "\u1F63", + "/omegaaspergraveiotasub": "\u1FA3", + "/omegaasperiotasub": "\u1FA1", + "/omegaaspertilde": "\u1F67", + "/omegaaspertildeiotasub": "\u1FA7", + "/omegaclosed": "\u0277", + "/omegacyr": "\u0461", + "/omegacyrillic": "\u0461", + "/omegafunc": "\u2375", + "/omegagrave": "\u1F7C", + "/omegagraveiotasub": "\u1FF2", + "/omegaiotasub": "\u1FF3", + "/omegalatinclosed": "\u0277", + "/omegalenis": "\u1F60", + "/omegalenisacute": "\u1F64", + "/omegalenisacuteiotasub": "\u1FA4", + "/omegalenisgrave": "\u1F62", + "/omegalenisgraveiotasub": "\u1FA2", + "/omegalenisiotasub": "\u1FA0", + "/omegalenistilde": "\u1F66", + "/omegalenistildeiotasub": "\u1FA6", + "/omegaroundcyr": "\u047B", + "/omegaroundcyrillic": "\u047B", + "/omegatilde": "\u1FF6", + "/omegatildeiotasub": "\u1FF7", + "/omegatitlocyr": "\u047D", + "/omegatitlocyrillic": "\u047D", + "/omegatonos": "\u03CE", + "/omegaunderlinefunc": "\u2379", + "/omgujarati": "\u0AD0", + "/omicron": "\u03BF", + "/omicronacute": "\u1F79", + "/omicronasper": "\u1F41", + "/omicronasperacute": "\u1F45", + "/omicronaspergrave": "\u1F43", + "/omicrongrave": "\u1F78", + "/omicronlenis": "\u1F40", + "/omicronlenisacute": "\u1F44", + "/omicronlenisgrave": "\u1F42", + "/omicrontonos": "\u03CC", + "/omonospace": "\uFF4F", + "/onExclamationMarkLeftRightArrowAbove": "\u1F51B", + "/oncomingAutomobile": "\u1F698", + "/oncomingBus": "\u1F68D", + "/oncomingFireEngine": "\u1F6F1", + "/oncomingPoliceCar": "\u1F694", + "/oncomingTaxi": "\u1F696", + "/one": "\u0031", + "/one.inferior": "\u2081", + "/one.roman": "\u2160", + "/one.romansmall": "\u2170", + "/oneButtonMouse": "\u1F5AF", + "/onearabic": "\u0661", + "/onebengali": "\u09E7", + "/onecircle": "\u2460", + "/onecircledbl": "\u24F5", + "/onecircleinversesansserif": "\u278A", + "/onecomma": "\u1F102", + "/onedeva": "\u0967", + "/onedotenleader": "\u2024", + "/onedotovertwodots": "\u2E2B", + "/oneeighth": "\u215B", + "/onefar": "\u06F1", + "/onefitted": "\uF6DC", + "/onefraction": "\u215F", + "/onegujarati": "\u0AE7", + "/onegurmukhi": "\u0A67", + "/onehackarabic": "\u0661", + "/onehalf": "\u00BD", + "/onehangzhou": "\u3021", + "/onehundred.roman": "\u216D", + "/onehundred.romansmall": "\u217D", + "/onehundredthousand.roman": "\u2188", + "/onehundredtwentypsquare": "\u1F1A4", + "/oneideographiccircled": "\u3280", + "/oneideographicparen": "\u3220", + "/oneinferior": "\u2081", + "/onemonospace": "\uFF11", + "/oneninth": "\u2151", + "/onenumeratorbengali": "\u09F4", + "/oneoldstyle": "\uF731", + "/oneparen": "\u2474", + "/oneparenthesized": "\u2474", + "/oneperiod": "\u2488", + "/onepersian": "\u06F1", + "/onequarter": "\u00BC", + "/oneroman": "\u2170", + "/oneseventh": "\u2150", + "/onesixth": "\u2159", + "/onesuperior": "\u00B9", + "/onethai": "\u0E51", + "/onethird": "\u2153", + "/onethousand.roman": "\u216F", + "/onethousand.romansmall": "\u217F", + "/onethousandcd.roman": "\u2180", + "/onsusquare": "\u3309", + "/oo": "\uA74F", + "/oogonek": "\u01EB", + "/oogonekmacron": "\u01ED", + "/oogurmukhi": "\u0A13", + "/oomatragurmukhi": "\u0A4B", + "/oomusquare": "\u330A", + "/oopen": "\u0254", + "/oparen": "\u24AA", + "/oparenthesized": "\u24AA", + "/openBook": "\u1F4D6", + "/openFileFolder": "\u1F4C2", + "/openFolder": "\u1F5C1", + "/openHandsSign": "\u1F450", + "/openLock": "\u1F513", + "/openMailboxLoweredFlag": "\u1F4ED", + "/openMailboxRaisedFlag": "\u1F4EC", + "/openbullet": "\u25E6", + "/openheadarrowleft": "\u21FD", + "/openheadarrowleftright": "\u21FF", + "/openheadarrowright": "\u21FE", + "/opensubset": "\u27C3", + "/opensuperset": "\u27C4", + "/ophiuchus": "\u26CE", + "/opposition": "\u260D", + "/opticalDisc": "\u1F4BF", + "/opticalDiscIcon": "\u1F5B8", + "/option": "\u2325", + "/orangeBook": "\u1F4D9", + "/ordfeminine": "\u00AA", + "/ordmasculine": "\u00BA", + "/ordotinside": "\u27C7", + "/original": "\u22B6", + "/ornateleftparenthesis": "\uFD3E", + "/ornaterightparenthesis": "\uFD3F", + "/orthodoxcross": "\u2626", + "/orthogonal": "\u221F", + "/orya:a": "\u0B05", + "/orya:aa": "\u0B06", + "/orya:aasign": "\u0B3E", + "/orya:ai": "\u0B10", + "/orya:ailengthmark": "\u0B56", + "/orya:aisign": "\u0B48", + "/orya:anusvara": "\u0B02", + "/orya:au": "\u0B14", + "/orya:aulengthmark": "\u0B57", + "/orya:ausign": "\u0B4C", + "/orya:avagraha": "\u0B3D", + "/orya:ba": "\u0B2C", + "/orya:bha": "\u0B2D", + "/orya:ca": "\u0B1A", + "/orya:candrabindu": "\u0B01", + "/orya:cha": "\u0B1B", + "/orya:da": "\u0B26", + "/orya:dda": "\u0B21", + "/orya:ddha": "\u0B22", + "/orya:dha": "\u0B27", + "/orya:e": "\u0B0F", + "/orya:eight": "\u0B6E", + "/orya:esign": "\u0B47", + "/orya:five": "\u0B6B", + "/orya:four": "\u0B6A", + "/orya:fractiononeeighth": "\u0B76", + "/orya:fractiononehalf": "\u0B73", + "/orya:fractiononequarter": "\u0B72", + "/orya:fractiononesixteenth": "\u0B75", + "/orya:fractionthreequarters": "\u0B74", + "/orya:fractionthreesixteenths": "\u0B77", + "/orya:ga": "\u0B17", + "/orya:gha": "\u0B18", + "/orya:ha": "\u0B39", + "/orya:i": "\u0B07", + "/orya:ii": "\u0B08", + "/orya:iisign": "\u0B40", + "/orya:isign": "\u0B3F", + "/orya:isshar": "\u0B70", + "/orya:ja": "\u0B1C", + "/orya:jha": "\u0B1D", + "/orya:ka": "\u0B15", + "/orya:kha": "\u0B16", + "/orya:la": "\u0B32", + "/orya:lla": "\u0B33", + "/orya:llvocal": "\u0B61", + "/orya:llvocalsign": "\u0B63", + "/orya:lvocal": "\u0B0C", + "/orya:lvocalsign": "\u0B62", + "/orya:ma": "\u0B2E", + "/orya:na": "\u0B28", + "/orya:nga": "\u0B19", + "/orya:nine": "\u0B6F", + "/orya:nna": "\u0B23", + "/orya:nukta": "\u0B3C", + "/orya:nya": "\u0B1E", + "/orya:o": "\u0B13", + "/orya:one": "\u0B67", + "/orya:osign": "\u0B4B", + "/orya:pa": "\u0B2A", + "/orya:pha": "\u0B2B", + "/orya:ra": "\u0B30", + "/orya:rha": "\u0B5D", + "/orya:rra": "\u0B5C", + "/orya:rrvocal": "\u0B60", + "/orya:rrvocalsign": "\u0B44", + "/orya:rvocal": "\u0B0B", + "/orya:rvocalsign": "\u0B43", + "/orya:sa": "\u0B38", + "/orya:seven": "\u0B6D", + "/orya:sha": "\u0B36", + "/orya:six": "\u0B6C", + "/orya:ssa": "\u0B37", + "/orya:ta": "\u0B24", + "/orya:tha": "\u0B25", + "/orya:three": "\u0B69", + "/orya:tta": "\u0B1F", + "/orya:ttha": "\u0B20", + "/orya:two": "\u0B68", + "/orya:u": "\u0B09", + "/orya:usign": "\u0B41", + "/orya:uu": "\u0B0A", + "/orya:uusign": "\u0B42", + "/orya:va": "\u0B35", + "/orya:virama": "\u0B4D", + "/orya:visarga": "\u0B03", + "/orya:wa": "\u0B71", + "/orya:ya": "\u0B2F", + "/orya:yya": "\u0B5F", + "/orya:zero": "\u0B66", + "/oscript": "\u2134", + "/oshortdeva": "\u0912", + "/oshortvowelsigndeva": "\u094A", + "/oslash": "\u00F8", + "/oslashacute": "\u01FF", + "/osmallhiragana": "\u3049", + "/osmallkatakana": "\u30A9", + "/osmallkatakanahalfwidth": "\uFF6B", + "/ostroke": "\uA74B", + "/ostrokeacute": "\u01FF", + "/osuperior": "\uF6F0", + "/otcyr": "\u047F", + "/otcyrillic": "\u047F", + "/otilde": "\u00F5", + "/otildeacute": "\u1E4D", + "/otildedieresis": "\u1E4F", + "/otildemacron": "\u022D", + "/ou": "\u0223", + "/oubopomofo": "\u3121", + "/ounce": "\u2125", + "/outboxTray": "\u1F4E4", + "/outerjoinfull": "\u27D7", + "/outerjoinleft": "\u27D5", + "/outerjoinright": "\u27D6", + "/outputpassiveup": "\u2392", + "/overlap": "\u1F5D7", + "/overline": "\u203E", + "/overlinecenterline": "\uFE4A", + "/overlinecmb": "\u0305", + "/overlinedashed": "\uFE49", + "/overlinedblwavy": "\uFE4C", + "/overlinewavy": "\uFE4B", + "/overscore": "\u00AF", + "/ovfullwidth": "\u3375", + "/ovowelsignbengali": "\u09CB", + "/ovowelsigndeva": "\u094B", + "/ovowelsigngujarati": "\u0ACB", + "/ox": "\u1F402", + "/p": "\u0070", + "/p.inferior": "\u209A", + "/paampsfullwidth": "\u3380", + "/paampssquare": "\u3380", + "/paasentosquare": "\u332B", + "/paatusquare": "\u332C", + "/pabengali": "\u09AA", + "/pacerek": "\uA989", + "/package": "\u1F4E6", + "/pacute": "\u1E55", + "/padeva": "\u092A", + "/pafullwidth": "\u33A9", + "/page": "\u1F5CF", + "/pageCircledText": "\u1F5DF", + "/pageCurl": "\u1F4C3", + "/pageFacingUp": "\u1F4C4", + "/pagedown": "\u21DF", + "/pager": "\u1F4DF", + "/pages": "\u1F5D0", + "/pageup": "\u21DE", + "/pagoda": "\u1F6D4", + "/pagujarati": "\u0AAA", + "/pagurmukhi": "\u0A2A", + "/pahiragana": "\u3071", + "/paiyannoithai": "\u0E2F", + "/pakatakana": "\u30D1", + "/palatalizationcyrilliccmb": "\u0484", + "/palatcmbcyr": "\u0484", + "/pallas": "\u26B4", + "/palmTree": "\u1F334", + "/palmbranch": "\u2E19", + "/palochkacyr": "\u04CF", + "/palochkacyrillic": "\u04C0", + "/pamurda": "\uA9A6", + "/pandaFace": "\u1F43C", + "/pangkatpada": "\uA9C7", + "/pangkon": "\uA9C0", + "/pangrangkep": "\uA9CF", + "/pansioskorean": "\u317F", + "/panyangga": "\uA980", + "/paperclip": "\u1F4CE", + "/paragraph": "\u00B6", + "/paragraphos": "\u2E0F", + "/paragraphosforked": "\u2E10", + "/paragraphosforkedreversed": "\u2E11", + "/paragraphseparator": "\u2029", + "/parallel": "\u2225", + "/parallelogramblack": "\u25B0", + "/parallelogramwhite": "\u25B1", + "/parenbottom": "\u23DD", + "/parendblleft": "\u2E28", + "/parendblright": "\u2E29", + "/parenextensionleft": "\u239C", + "/parenextensionright": "\u239F", + "/parenflatleft": "\u27EE", + "/parenflatright": "\u27EF", + "/parenhookupleft": "\u239B", + "/parenhookupright": "\u239E", + "/parenleft": "\u0028", + "/parenleft.inferior": "\u208D", + "/parenleft.superior": "\u207D", + "/parenleftaltonearabic": "\uFD3E", + "/parenleftbt": "\uF8ED", + "/parenleftex": "\uF8EC", + "/parenleftinferior": "\u208D", + "/parenleftmonospace": "\uFF08", + "/parenleftsmall": "\uFE59", + "/parenleftsuperior": "\u207D", + "/parenlefttp": "\uF8EB", + "/parenleftvertical": "\uFE35", + "/parenlowerhookleft": "\u239D", + "/parenlowerhookright": "\u23A0", + "/parenright": "\u0029", + "/parenright.inferior": "\u208E", + "/parenright.superior": "\u207E", + "/parenrightaltonearabic": "\uFD3F", + "/parenrightbt": "\uF8F8", + "/parenrightex": "\uF8F7", + "/parenrightinferior": "\u208E", + "/parenrightmonospace": "\uFF09", + "/parenrightsmall": "\uFE5A", + "/parenrightsuperior": "\u207E", + "/parenrighttp": "\uF8F6", + "/parenrightvertical": "\uFE36", + "/parentop": "\u23DC", + "/partalternationmark": "\u303D", + "/partialdiff": "\u2202", + "/partnership": "\u3250", + "/partyPopper": "\u1F389", + "/paseq:hb": "\u05C0", + "/paseqhebrew": "\u05C0", + "/pashta:hb": "\u0599", + "/pashtahebrew": "\u0599", + "/pasquare": "\u33A9", + "/passengerShip": "\u1F6F3", + "/passivedown": "\u2391", + "/passportControl": "\u1F6C2", + "/patah": "\u05B7", + "/patah11": "\u05B7", + "/patah1d": "\u05B7", + "/patah2a": "\u05B7", + "/patah:hb": "\u05B7", + "/patahhebrew": "\u05B7", + "/patahnarrowhebrew": "\u05B7", + "/patahquarterhebrew": "\u05B7", + "/patahwidehebrew": "\u05B7", + "/pawPrints": "\u1F43E", + "/pawnblack": "\u265F", + "/pawnwhite": "\u2659", + "/pazer:hb": "\u05A1", + "/pazerhebrew": "\u05A1", + "/pbopomofo": "\u3106", + "/pcfullwidth": "\u3376", + "/pcircle": "\u24DF", + "/pdot": "\u1E57", + "/pdotaccent": "\u1E57", + "/pe": "\u05E4", + "/pe:hb": "\u05E4", + "/peace": "\u262E", + "/peach": "\u1F351", + "/pear": "\u1F350", + "/pecyr": "\u043F", + "/pecyrillic": "\u043F", + "/pedagesh": "\uFB44", + "/pedageshhebrew": "\uFB44", + "/pedestrian": "\u1F6B6", + "/peezisquare": "\u333B", + "/pefinaldageshhebrew": "\uFB43", + "/peh.fina": "\uFB57", + "/peh.init": "\uFB58", + "/peh.isol": "\uFB56", + "/peh.medi": "\uFB59", + "/peharabic": "\u067E", + "/peharmenian": "\u057A", + "/pehebrew": "\u05E4", + "/peheh": "\u06A6", + "/peheh.fina": "\uFB6F", + "/peheh.init": "\uFB70", + "/peheh.isol": "\uFB6E", + "/peheh.medi": "\uFB71", + "/pehfinalarabic": "\uFB57", + "/pehinitialarabic": "\uFB58", + "/pehiragana": "\u307A", + "/pehmedialarabic": "\uFB59", + "/pehookcyr": "\u04A7", + "/pekatakana": "\u30DA", + "/pemiddlehookcyrillic": "\u04A7", + "/penOverStampedEnvelope": "\u1F586", + "/pengkalconsonant": "\uA9BE", + "/penguin": "\u1F427", + "/penihisquare": "\u3338", + "/pensiveFace": "\u1F614", + "/pensusquare": "\u333A", + "/pentagram": "\u26E4", + "/pentasememetrical": "\u23D9", + "/pepetvowel": "\uA9BC", + "/per": "\u214C", + "/perafehebrew": "\uFB4E", + "/percent": "\u0025", + "/percentarabic": "\u066A", + "/percentmonospace": "\uFF05", + "/percentsmall": "\uFE6A", + "/percussivebidental": "\u02AD", + "/percussivebilabial": "\u02AC", + "/performingArts": "\u1F3AD", + "/period": "\u002E", + "/periodarmenian": "\u0589", + "/periodcentered": "\u00B7", + "/periodhalfwidth": "\uFF61", + "/periodinferior": "\uF6E7", + "/periodmonospace": "\uFF0E", + "/periodsmall": "\uFE52", + "/periodsuperior": "\uF6E8", + "/periodurdu": "\u06D4", + "/perispomenigreekcmb": "\u0342", + "/permanentpaper": "\u267E", + "/permille": "\u0609", + "/perpendicular": "\u22A5", + "/perseveringFace": "\u1F623", + "/personBlondHair": "\u1F471", + "/personBowingDeeply": "\u1F647", + "/personFrowning": "\u1F64D", + "/personRaisingBothHandsInCelebration": "\u1F64C", + "/personWithFoldedHands": "\u1F64F", + "/personWithPoutingFace": "\u1F64E", + "/personalComputer": "\u1F4BB", + "/personball": "\u26F9", + "/perspective": "\u2306", + "/pertenthousandsign": "\u2031", + "/perthousand": "\u2030", + "/peseta": "\u20A7", + "/peso": "\u20B1", + "/pesosquare": "\u3337", + "/petailcyr": "\u0525", + "/pewithdagesh:hb": "\uFB44", + "/pewithrafe:hb": "\uFB4E", + "/pffullwidth": "\u338A", + "/pflourish": "\uA753", + "/pfsquare": "\u338A", + "/phabengali": "\u09AB", + "/phadeva": "\u092B", + "/phagujarati": "\u0AAB", + "/phagurmukhi": "\u0A2B", + "/pharyngealvoicedfricative": "\u0295", + "/phfullwidth": "\u33D7", + "/phi": "\u03C6", + "/phi.math": "\u03D5", + "/phi1": "\u03D5", + "/phieuphacirclekorean": "\u327A", + "/phieuphaparenkorean": "\u321A", + "/phieuphcirclekorean": "\u326C", + "/phieuphkorean": "\u314D", + "/phieuphparenkorean": "\u320C", + "/philatin": "\u0278", + "/phinthuthai": "\u0E3A", + "/phisymbolgreek": "\u03D5", + "/phitailless": "\u2C77", + "/phon:AEsmall": "\u1D01", + "/phon:Aemod": "\u1D2D", + "/phon:Amod": "\u1D2C", + "/phon:Asmall": "\u1D00", + "/phon:Bbarmod": "\u1D2F", + "/phon:Bbarsmall": "\u1D03", + "/phon:Bmod": "\u1D2E", + "/phon:Csmall": "\u1D04", + "/phon:Dmod": "\u1D30", + "/phon:Dsmall": "\u1D05", + "/phon:ENcyrmod": "\u1D78", + "/phon:Elsmallcyr": "\u1D2B", + "/phon:Emod": "\u1D31", + "/phon:Ereversedmod": "\u1D32", + "/phon:Esmall": "\u1D07", + "/phon:Ethsmall": "\u1D06", + "/phon:Ezhsmall": "\u1D23", + "/phon:Gmod": "\u1D33", + "/phon:Hmod": "\u1D34", + "/phon:Imod": "\u1D35", + "/phon:Ismallmod": "\u1DA6", + "/phon:Ismallstroke": "\u1D7B", + "/phon:Istrokesmallmod": "\u1DA7", + "/phon:Jmod": "\u1D36", + "/phon:Jsmall": "\u1D0A", + "/phon:Kmod": "\u1D37", + "/phon:Ksmall": "\u1D0B", + "/phon:Lmod": "\u1D38", + "/phon:Lsmallmod": "\u1DAB", + "/phon:Lsmallstroke": "\u1D0C", + "/phon:Mmod": "\u1D39", + "/phon:Msmall": "\u1D0D", + "/phon:Nmod": "\u1D3A", + "/phon:Nreversedmod": "\u1D3B", + "/phon:Nsmallmod": "\u1DB0", + "/phon:Nsmallreversed": "\u1D0E", + "/phon:OUsmall": "\u1D15", + "/phon:Omod": "\u1D3C", + "/phon:Oopensmall": "\u1D10", + "/phon:Osmall": "\u1D0F", + "/phon:Oumod": "\u1D3D", + "/phon:Pmod": "\u1D3E", + "/phon:Psmall": "\u1D18", + "/phon:Rmod": "\u1D3F", + "/phon:Rsmallreversed": "\u1D19", + "/phon:Rsmallturned": "\u1D1A", + "/phon:Tmod": "\u1D40", + "/phon:Tsmall": "\u1D1B", + "/phon:Umod": "\u1D41", + "/phon:Usmall": "\u1D1C", + "/phon:Usmallmod": "\u1DB8", + "/phon:Usmallstroke": "\u1D7E", + "/phon:Vsmall": "\u1D20", + "/phon:Wmod": "\u1D42", + "/phon:Wsmall": "\u1D21", + "/phon:Zsmall": "\u1D22", + "/phon:aeturned": "\u1D02", + "/phon:aeturnedmod": "\u1D46", + "/phon:ain": "\u1D25", + "/phon:ainmod": "\u1D5C", + "/phon:alphamod": "\u1D45", + "/phon:alpharetroflexhook": "\u1D90", + "/phon:alphaturnedmod": "\u1D9B", + "/phon:amod": "\u1D43", + "/phon:aretroflexhook": "\u1D8F", + "/phon:aturnedmod": "\u1D44", + "/phon:betamod": "\u1D5D", + "/phon:bmiddletilde": "\u1D6C", + "/phon:bmod": "\u1D47", + "/phon:bpalatalhook": "\u1D80", + "/phon:ccurlmod": "\u1D9D", + "/phon:chimod": "\u1D61", + "/phon:cmod": "\u1D9C", + "/phon:deltamod": "\u1D5F", + "/phon:dhooktail": "\u1D91", + "/phon:dmiddletilde": "\u1D6D", + "/phon:dmod": "\u1D48", + "/phon:dotlessjstrokemod": "\u1DA1", + "/phon:dpalatalhook": "\u1D81", + "/phon:emod": "\u1D49", + "/phon:engmod": "\u1D51", + "/phon:eopenmod": "\u1D4B", + "/phon:eopenretroflexhook": "\u1D93", + "/phon:eopenreversedmod": "\u1D9F", + "/phon:eopenreversedretroflexhook": "\u1D94", + "/phon:eopenturned": "\u1D08", + "/phon:eopenturnedmod": "\u1D4C", + "/phon:eretroflexhook": "\u1D92", + "/phon:eshmod": "\u1DB4", + "/phon:eshpalatalhook": "\u1D8B", + "/phon:eshretroflexhook": "\u1D98", + "/phon:ethmod": "\u1D9E", + "/phon:ezhmod": "\u1DBE", + "/phon:ezhretroflexhook": "\u1D9A", + "/phon:fmiddletilde": "\u1D6E", + "/phon:fmod": "\u1DA0", + "/phon:fpalatalhook": "\u1D82", + "/phon:ginsular": "\u1D79", + "/phon:gmod": "\u1D4D", + "/phon:gpalatalhook": "\u1D83", + "/phon:gr:Gammasmall": "\u1D26", + "/phon:gr:Lambdasmall": "\u1D27", + "/phon:gr:Pismall": "\u1D28", + "/phon:gr:Psismall": "\u1D2A", + "/phon:gr:RsmallHO": "\u1D29", + "/phon:gr:betasubscript": "\u1D66", + "/phon:gr:chisubscript": "\u1D6A", + "/phon:gr:gammamod": "\u1D5E", + "/phon:gr:gammasubscript": "\u1D67", + "/phon:gr:phimod": "\u1D60", + "/phon:gr:phisubscript": "\u1D69", + "/phon:gr:rhosubscript": "\u1D68", + "/phon:gscriptmod": "\u1DA2", + "/phon:gturned": "\u1D77", + "/phon:hturnedmod": "\u1DA3", + "/phon:iotamod": "\u1DA5", + "/phon:iotastroke": "\u1D7C", + "/phon:iretroflexhook": "\u1D96", + "/phon:istrokemod": "\u1DA4", + "/phon:isubscript": "\u1D62", + "/phon:iturned": "\u1D09", + "/phon:iturnedmod": "\u1D4E", + "/phon:jcrossedtailmod": "\u1DA8", + "/phon:kmod": "\u1D4F", + "/phon:kpalatalhook": "\u1D84", + "/phon:lpalatalhook": "\u1D85", + "/phon:lpalatalhookmod": "\u1DAA", + "/phon:lretroflexhookmod": "\u1DA9", + "/phon:mhookmod": "\u1DAC", + "/phon:mlonglegturnedmod": "\u1DAD", + "/phon:mmiddletilde": "\u1D6F", + "/phon:mmod": "\u1D50", + "/phon:mpalatalhook": "\u1D86", + "/phon:mturnedmod": "\u1D5A", + "/phon:mturnedsideways": "\u1D1F", + "/phon:nlefthookmod": "\u1DAE", + "/phon:nmiddletilde": "\u1D70", + "/phon:npalatalhook": "\u1D87", + "/phon:nretroflexhookmod": "\u1DAF", + "/phon:obarmod": "\u1DB1", + "/phon:obottomhalf": "\u1D17", + "/phon:obottomhalfmod": "\u1D55", + "/phon:oeturned": "\u1D14", + "/phon:omod": "\u1D52", + "/phon:oopenmod": "\u1D53", + "/phon:oopenretroflexhook": "\u1D97", + "/phon:oopensideways": "\u1D12", + "/phon:osideways": "\u1D11", + "/phon:ostrokesideways": "\u1D13", + "/phon:otophalf": "\u1D16", + "/phon:otophalfmod": "\u1D54", + "/phon:phimod": "\u1DB2", + "/phon:pmiddletilde": "\u1D71", + "/phon:pmod": "\u1D56", + "/phon:ppalatalhook": "\u1D88", + "/phon:pstroke": "\u1D7D", + "/phon:rfishmiddletilde": "\u1D73", + "/phon:rmiddletilde": "\u1D72", + "/phon:rpalatalhook": "\u1D89", + "/phon:rsubscript": "\u1D63", + "/phon:schwamod": "\u1D4A", + "/phon:schwaretroflexhook": "\u1D95", + "/phon:shookmod": "\u1DB3", + "/phon:smiddletilde": "\u1D74", + "/phon:spalatalhook": "\u1D8A", + "/phon:spirantvoicedlaryngeal": "\u1D24", + "/phon:thetamod": "\u1DBF", + "/phon:thstrike": "\u1D7A", + "/phon:tmiddletilde": "\u1D75", + "/phon:tmod": "\u1D57", + "/phon:tpalatalhookmod": "\u1DB5", + "/phon:ubarmod": "\u1DB6", + "/phon:ue": "\u1D6B", + "/phon:umod": "\u1D58", + "/phon:upsilonmod": "\u1DB7", + "/phon:upsilonstroke": "\u1D7F", + "/phon:uretroflexhook": "\u1D99", + "/phon:usideways": "\u1D1D", + "/phon:usidewaysdieresised": "\u1D1E", + "/phon:usidewaysmod": "\u1D59", + "/phon:usubscript": "\u1D64", + "/phon:vhookmod": "\u1DB9", + "/phon:vmod": "\u1D5B", + "/phon:vpalatalhook": "\u1D8C", + "/phon:vsubscript": "\u1D65", + "/phon:vturnedmod": "\u1DBA", + "/phon:xpalatalhook": "\u1D8D", + "/phon:zcurlmod": "\u1DBD", + "/phon:zmiddletilde": "\u1D76", + "/phon:zmod": "\u1DBB", + "/phon:zpalatalhook": "\u1D8E", + "/phon:zretroflexhookmod": "\u1DBC", + "/phook": "\u01A5", + "/phophanthai": "\u0E1E", + "/phophungthai": "\u0E1C", + "/phosamphaothai": "\u0E20", + "/pi": "\u03C0", + "/pi.math": "\u03D6", + "/piasutorusquare": "\u332E", + "/pick": "\u26CF", + "/pidblstruck": "\u213C", + "/pieupacirclekorean": "\u3273", + "/pieupaparenkorean": "\u3213", + "/pieupcieuckorean": "\u3176", + "/pieupcirclekorean": "\u3265", + "/pieupkiyeokkorean": "\u3172", + "/pieupkorean": "\u3142", + "/pieupparenkorean": "\u3205", + "/pieupsioskiyeokkorean": "\u3174", + "/pieupsioskorean": "\u3144", + "/pieupsiostikeutkorean": "\u3175", + "/pieupthieuthkorean": "\u3177", + "/pieuptikeutkorean": "\u3173", + "/pig": "\u1F416", + "/pigFace": "\u1F437", + "/pigNose": "\u1F43D", + "/pihiragana": "\u3074", + "/pikatakana": "\u30D4", + "/pikosquare": "\u3330", + "/pikurusquare": "\u332F", + "/pilcrowsignreversed": "\u204B", + "/pileOfPoo": "\u1F4A9", + "/pill": "\u1F48A", + "/pineDecoration": "\u1F38D", + "/pineapple": "\u1F34D", + "/pisces": "\u2653", + "/piselehpada": "\uA9CC", + "/pistol": "\u1F52B", + "/pisymbolgreek": "\u03D6", + "/pitchfork": "\u22D4", + "/piwrarmenian": "\u0583", + "/placeOfWorship": "\u1F6D0", + "/placeofinterestsign": "\u2318", + "/planck": "\u210E", + "/plancktwopi": "\u210F", + "/plus": "\u002B", + "/plus.inferior": "\u208A", + "/plus.superior": "\u207A", + "/plusbelowcmb": "\u031F", + "/pluscircle": "\u2295", + "/plusminus": "\u00B1", + "/plusmod": "\u02D6", + "/plusmonospace": "\uFF0B", + "/plussignalt:hb": "\uFB29", + "/plussignmod": "\u02D6", + "/plussmall": "\uFE62", + "/plussuperior": "\u207A", + "/pluto": "\u2647", + "/pmfullwidth": "\u33D8", + "/pmonospace": "\uFF50", + "/pmsquare": "\u33D8", + "/pocketCalculator": "\u1F5A9", + "/poeticverse": "\u060E", + "/pohiragana": "\u307D", + "/pointerleftblack": "\u25C4", + "/pointerleftwhite": "\u25C5", + "/pointerrightblack": "\u25BA", + "/pointerrightwhite": "\u25BB", + "/pointingindexdownwhite": "\u261F", + "/pointingindexleftblack": "\u261A", + "/pointingindexleftwhite": "\u261C", + "/pointingindexrightblack": "\u261B", + "/pointingindexrightwhite": "\u261E", + "/pointingindexupwhite": "\u261D", + "/pointingtriangledownheavywhite": "\u26DB", + "/pointosquare": "\u333D", + "/pointring": "\u2E30", + "/pokatakana": "\u30DD", + "/pokrytiecmbcyr": "\u0487", + "/policeCar": "\u1F693", + "/policeCarsRevolvingLight": "\u1F6A8", + "/policeOfficer": "\u1F46E", + "/pondosquare": "\u3340", + "/poodle": "\u1F429", + "/popcorn": "\u1F37F", + "/popdirectionalformatting": "\u202C", + "/popdirectionalisolate": "\u2069", + "/poplathai": "\u0E1B", + "/portableStereo": "\u1F4FE", + "/positionindicator": "\u2316", + "/postalHorn": "\u1F4EF", + "/postalmark": "\u3012", + "/postalmarkface": "\u3020", + "/postbox": "\u1F4EE", + "/potOfFood": "\u1F372", + "/potableWater": "\u1F6B0", + "/pouch": "\u1F45D", + "/poultryLeg": "\u1F357", + "/poutingCatFace": "\u1F63E", + "/poutingFace": "\u1F621", + "/power": "\u23FB", + "/poweron": "\u23FD", + "/poweronoff": "\u23FC", + "/powersleep": "\u23FE", + "/pparen": "\u24AB", + "/pparenthesized": "\u24AB", + "/ppmfullwidth": "\u33D9", + "/prayerBeads": "\u1F4FF", + "/precedes": "\u227A", + "/precedesbutnotequivalent": "\u22E8", + "/precedesorequal": "\u227C", + "/precedesorequivalent": "\u227E", + "/precedesunderrelation": "\u22B0", + "/prescription": "\u211E", + "/preversedepigraphic": "\uA7FC", + "/previouspage": "\u2397", + "/prfullwidth": "\u33DA", + "/primedblmod": "\u02BA", + "/primemod": "\u02B9", + "/primereversed": "\u2035", + "/princess": "\u1F478", + "/printer": "\u1F5A8", + "/printerIcon": "\u1F5B6", + "/printideographiccircled": "\u329E", + "/printscreen": "\u2399", + "/product": "\u220F", + "/prohibitedSign": "\u1F6C7", + "/projective": "\u2305", + "/prolongedkana": "\u30FC", + "/propellor": "\u2318", + "/propersubset": "\u2282", + "/propersuperset": "\u2283", + "/propertyline": "\u214A", + "/proportion": "\u2237", + "/proportional": "\u221D", + "/psfullwidth": "\u33B0", + "/psi": "\u03C8", + "/psicyr": "\u0471", + "/psicyrillic": "\u0471", + "/psilicmbcyr": "\u0486", + "/psilipneumatacyrilliccmb": "\u0486", + "/pssquare": "\u33B0", + "/pstrokedescender": "\uA751", + "/ptail": "\uA755", + "/publicAddressLoudspeaker": "\u1F4E2", + "/puhiragana": "\u3077", + "/pukatakana": "\u30D7", + "/punctuationspace": "\u2008", + "/purpleHeart": "\u1F49C", + "/purse": "\u1F45B", + "/pushpin": "\u1F4CC", + "/putLitterInItsPlace": "\u1F6AE", + "/pvfullwidth": "\u33B4", + "/pvsquare": "\u33B4", + "/pwfullwidth": "\u33BA", + "/pwsquare": "\u33BA", + "/q": "\u0071", + "/qacyr": "\u051B", + "/qadeva": "\u0958", + "/qadma:hb": "\u05A8", + "/qadmahebrew": "\u05A8", + "/qaf": "\u0642", + "/qaf.fina": "\uFED6", + "/qaf.init": "\uFED7", + "/qaf.init_alefmaksura.fina": "\uFC35", + "/qaf.init_hah.fina": "\uFC33", + "/qaf.init_hah.medi": "\uFCC2", + "/qaf.init_meem.fina": "\uFC34", + "/qaf.init_meem.medi": "\uFCC3", + "/qaf.init_meem.medi_hah.medi": "\uFDB4", + "/qaf.init_yeh.fina": "\uFC36", + "/qaf.isol": "\uFED5", + "/qaf.medi": "\uFED8", + "/qaf.medi_alefmaksura.fina": "\uFC7E", + "/qaf.medi_meem.medi_hah.fina": "\uFD7E", + "/qaf.medi_meem.medi_meem.fina": "\uFD7F", + "/qaf.medi_meem.medi_yeh.fina": "\uFDB2", + "/qaf.medi_yeh.fina": "\uFC7F", + "/qaf_lam_alefmaksuraabove": "\u06D7", + "/qafarabic": "\u0642", + "/qafdotabove": "\u06A7", + "/qaffinalarabic": "\uFED6", + "/qafinitialarabic": "\uFED7", + "/qafmedialarabic": "\uFED8", + "/qafthreedotsabove": "\u06A8", + "/qamats": "\u05B8", + "/qamats10": "\u05B8", + "/qamats1a": "\u05B8", + "/qamats1c": "\u05B8", + "/qamats27": "\u05B8", + "/qamats29": "\u05B8", + "/qamats33": "\u05B8", + "/qamats:hb": "\u05B8", + "/qamatsQatan:hb": "\u05C7", + "/qamatsde": "\u05B8", + "/qamatshebrew": "\u05B8", + "/qamatsnarrowhebrew": "\u05B8", + "/qamatsqatanhebrew": "\u05B8", + "/qamatsqatannarrowhebrew": "\u05B8", + "/qamatsqatanquarterhebrew": "\u05B8", + "/qamatsqatanwidehebrew": "\u05B8", + "/qamatsquarterhebrew": "\u05B8", + "/qamatswidehebrew": "\u05B8", + "/qarneFarah:hb": "\u059F", + "/qarneyparahebrew": "\u059F", + "/qbopomofo": "\u3111", + "/qcircle": "\u24E0", + "/qdiagonalstroke": "\uA759", + "/qhook": "\u02A0", + "/qhooktail": "\u024B", + "/qmonospace": "\uFF51", + "/qof": "\u05E7", + "/qof:hb": "\u05E7", + "/qofdagesh": "\uFB47", + "/qofdageshhebrew": "\uFB47", + "/qofhatafpatah": "\u05E7", + "/qofhatafpatahhebrew": "\u05E7", + "/qofhatafsegol": "\u05E7", + "/qofhatafsegolhebrew": "\u05E7", + "/qofhebrew": "\u05E7", + "/qofhiriq": "\u05E7", + "/qofhiriqhebrew": "\u05E7", + "/qofholam": "\u05E7", + "/qofholamhebrew": "\u05E7", + "/qofpatah": "\u05E7", + "/qofpatahhebrew": "\u05E7", + "/qofqamats": "\u05E7", + "/qofqamatshebrew": "\u05E7", + "/qofqubuts": "\u05E7", + "/qofqubutshebrew": "\u05E7", + "/qofsegol": "\u05E7", + "/qofsegolhebrew": "\u05E7", + "/qofsheva": "\u05E7", + "/qofshevahebrew": "\u05E7", + "/qoftsere": "\u05E7", + "/qoftserehebrew": "\u05E7", + "/qofwithdagesh:hb": "\uFB47", + "/qparen": "\u24AC", + "/qparenthesized": "\u24AC", + "/qpdigraph": "\u0239", + "/qstrokedescender": "\uA757", + "/quadarrowdownfunc": "\u2357", + "/quadarrowleftfunc": "\u2347", + "/quadarrowrightfunc": "\u2348", + "/quadarrowupfunc": "\u2350", + "/quadbackslashfunc": "\u2342", + "/quadcaretdownfunc": "\u234C", + "/quadcaretupfunc": "\u2353", + "/quadcirclefunc": "\u233C", + "/quadcolonfunc": "\u2360", + "/quaddelfunc": "\u2354", + "/quaddeltafunc": "\u234D", + "/quaddiamondfunc": "\u233A", + "/quaddividefunc": "\u2339", + "/quadequalfunc": "\u2338", + "/quadfunc": "\u2395", + "/quadgreaterfunc": "\u2344", + "/quadjotfunc": "\u233B", + "/quadlessfunc": "\u2343", + "/quadnotequalfunc": "\u236F", + "/quadquestionfunc": "\u2370", + "/quadrantLowerLeft": "\u2596", + "/quadrantLowerRight": "\u2597", + "/quadrantUpperLeft": "\u2598", + "/quadrantUpperLeftAndLowerLeftAndLowerRight": "\u2599", + "/quadrantUpperLeftAndLowerRight": "\u259A", + "/quadrantUpperLeftAndUpperRightAndLowerLeft": "\u259B", + "/quadrantUpperLeftAndUpperRightAndLowerRight": "\u259C", + "/quadrantUpperRight": "\u259D", + "/quadrantUpperRightAndLowerLeft": "\u259E", + "/quadrantUpperRightAndLowerLeftAndLowerRight": "\u259F", + "/quadrupleminute": "\u2057", + "/quadslashfunc": "\u2341", + "/quarternote": "\u2669", + "/qubuts": "\u05BB", + "/qubuts18": "\u05BB", + "/qubuts25": "\u05BB", + "/qubuts31": "\u05BB", + "/qubuts:hb": "\u05BB", + "/qubutshebrew": "\u05BB", + "/qubutsnarrowhebrew": "\u05BB", + "/qubutsquarterhebrew": "\u05BB", + "/qubutswidehebrew": "\u05BB", + "/queenblack": "\u265B", + "/queenwhite": "\u2655", + "/question": "\u003F", + "/questionarabic": "\u061F", + "/questionarmenian": "\u055E", + "/questiondbl": "\u2047", + "/questiondown": "\u00BF", + "/questiondownsmall": "\uF7BF", + "/questionedequal": "\u225F", + "/questionexclamationmark": "\u2048", + "/questiongreek": "\u037E", + "/questionideographiccircled": "\u3244", + "/questionmonospace": "\uFF1F", + "/questionreversed": "\u2E2E", + "/questionsmall": "\uF73F", + "/quincunx": "\u26BB", + "/quotedbl": "\u0022", + "/quotedblbase": "\u201E", + "/quotedblleft": "\u201C", + "/quotedbllowreversed": "\u2E42", + "/quotedblmonospace": "\uFF02", + "/quotedblprime": "\u301E", + "/quotedblprimereversed": "\u301D", + "/quotedblreversed": "\u201F", + "/quotedblright": "\u201D", + "/quoteleft": "\u2018", + "/quoteleftreversed": "\u201B", + "/quotequadfunc": "\u235E", + "/quotereversed": "\u201B", + "/quoteright": "\u2019", + "/quoterightn": "\u0149", + "/quotesinglbase": "\u201A", + "/quotesingle": "\u0027", + "/quotesinglemonospace": "\uFF07", + "/quoteunderlinefunc": "\u2358", + "/r": "\u0072", + "/raagung": "\uA9AC", + "/raarmenian": "\u057C", + "/rabbit": "\u1F407", + "/rabbitFace": "\u1F430", + "/rabengali": "\u09B0", + "/racingCar": "\u1F3CE", + "/racingMotorcycle": "\u1F3CD", + "/racirclekatakana": "\u32F6", + "/racute": "\u0155", + "/radeva": "\u0930", + "/radfullwidth": "\u33AD", + "/radical": "\u221A", + "/radicalbottom": "\u23B7", + "/radicalex": "\uF8E5", + "/radio": "\u1F4FB", + "/radioButton": "\u1F518", + "/radioactive": "\u2622", + "/radovers2fullwidth": "\u33AF", + "/radoversfullwidth": "\u33AE", + "/radoverssquare": "\u33AE", + "/radoverssquaredsquare": "\u33AF", + "/radsquare": "\u33AD", + "/rafe": "\u05BF", + "/rafe:hb": "\u05BF", + "/rafehebrew": "\u05BF", + "/ragujarati": "\u0AB0", + "/ragurmukhi": "\u0A30", + "/rahiragana": "\u3089", + "/railwayCar": "\u1F683", + "/railwayTrack": "\u1F6E4", + "/rain": "\u26C6", + "/rainbow": "\u1F308", + "/raisedHandFingersSplayed": "\u1F590", + "/raisedHandPartBetweenMiddleAndRingFingers": "\u1F596", + "/raisedmcsign": "\u1F16A", + "/raisedmdsign": "\u1F16B", + "/rakatakana": "\u30E9", + "/rakatakanahalfwidth": "\uFF97", + "/ralowerdiagonalbengali": "\u09F1", + "/ram": "\u1F40F", + "/ramiddlediagonalbengali": "\u09F0", + "/ramshorn": "\u0264", + "/rat": "\u1F400", + "/ratio": "\u2236", + "/ray": "\u0608", + "/rbopomofo": "\u3116", + "/rcaron": "\u0159", + "/rcedilla": "\u0157", + "/rcircle": "\u24E1", + "/rcommaaccent": "\u0157", + "/rdblgrave": "\u0211", + "/rdot": "\u1E59", + "/rdotaccent": "\u1E59", + "/rdotbelow": "\u1E5B", + "/rdotbelowmacron": "\u1E5D", + "/reachideographicparen": "\u3243", + "/recirclekatakana": "\u32F9", + "/recreationalVehicle": "\u1F699", + "/rectangleblack": "\u25AC", + "/rectangleverticalblack": "\u25AE", + "/rectangleverticalwhite": "\u25AF", + "/rectanglewhite": "\u25AD", + "/recycledpaper": "\u267C", + "/recyclefiveplastics": "\u2677", + "/recyclefourplastics": "\u2676", + "/recyclegeneric": "\u267A", + "/recycleoneplastics": "\u2673", + "/recyclepartiallypaper": "\u267D", + "/recyclesevenplastics": "\u2679", + "/recyclesixplastics": "\u2678", + "/recyclethreeplastics": "\u2675", + "/recycletwoplastics": "\u2674", + "/recycleuniversal": "\u2672", + "/recycleuniversalblack": "\u267B", + "/redApple": "\u1F34E", + "/redTriangleDOwn": "\u1F53B", + "/redTriangleUp": "\u1F53A", + "/referencemark": "\u203B", + "/reflexsubset": "\u2286", + "/reflexsuperset": "\u2287", + "/regionalindicatorsymbollettera": "\u1F1E6", + "/regionalindicatorsymbolletterb": "\u1F1E7", + "/regionalindicatorsymbolletterc": "\u1F1E8", + "/regionalindicatorsymbolletterd": "\u1F1E9", + "/regionalindicatorsymbollettere": "\u1F1EA", + "/regionalindicatorsymbolletterf": "\u1F1EB", + "/regionalindicatorsymbolletterg": "\u1F1EC", + "/regionalindicatorsymbolletterh": "\u1F1ED", + "/regionalindicatorsymbolletteri": "\u1F1EE", + "/regionalindicatorsymbolletterj": "\u1F1EF", + "/regionalindicatorsymbolletterk": "\u1F1F0", + "/regionalindicatorsymbolletterl": "\u1F1F1", + "/regionalindicatorsymbolletterm": "\u1F1F2", + "/regionalindicatorsymbollettern": "\u1F1F3", + "/regionalindicatorsymbollettero": "\u1F1F4", + "/regionalindicatorsymbolletterp": "\u1F1F5", + "/regionalindicatorsymbolletterq": "\u1F1F6", + "/regionalindicatorsymbolletterr": "\u1F1F7", + "/regionalindicatorsymbolletters": "\u1F1F8", + "/regionalindicatorsymbollettert": "\u1F1F9", + "/regionalindicatorsymbolletteru": "\u1F1FA", + "/regionalindicatorsymbolletterv": "\u1F1FB", + "/regionalindicatorsymbolletterw": "\u1F1FC", + "/regionalindicatorsymbolletterx": "\u1F1FD", + "/regionalindicatorsymbollettery": "\u1F1FE", + "/regionalindicatorsymbolletterz": "\u1F1FF", + "/registered": "\u00AE", + "/registersans": "\uF8E8", + "/registerserif": "\uF6DA", + "/reh.fina": "\uFEAE", + "/reh.init_superscriptalef.fina": "\uFC5C", + "/reh.isol": "\uFEAD", + "/rehHamzaAbove": "\u076C", + "/rehSmallTahTwoDots": "\u0771", + "/rehStroke": "\u075B", + "/rehTwoDotsVerticallyAbove": "\u076B", + "/rehVabove": "\u0692", + "/rehVbelow": "\u0695", + "/reharabic": "\u0631", + "/reharmenian": "\u0580", + "/rehdotbelow": "\u0694", + "/rehdotbelowdotabove": "\u0696", + "/rehfinalarabic": "\uFEAE", + "/rehfourdotsabove": "\u0699", + "/rehinvertedV": "\u06EF", + "/rehiragana": "\u308C", + "/rehring": "\u0693", + "/rehtwodotsabove": "\u0697", + "/rehyehaleflamarabic": "\u0631", + "/rekatakana": "\u30EC", + "/rekatakanahalfwidth": "\uFF9A", + "/relievedFace": "\u1F60C", + "/religionideographiccircled": "\u32AA", + "/reminderRibbon": "\u1F397", + "/remusquare": "\u3355", + "/rentogensquare": "\u3356", + "/replacementchar": "\uFFFD", + "/replacementcharobj": "\uFFFC", + "/representideographicparen": "\u3239", + "/rerengganleft": "\uA9C1", + "/rerengganright": "\uA9C2", + "/resh": "\u05E8", + "/resh:hb": "\u05E8", + "/reshdageshhebrew": "\uFB48", + "/reshhatafpatah": "\u05E8", + "/reshhatafpatahhebrew": "\u05E8", + "/reshhatafsegol": "\u05E8", + "/reshhatafsegolhebrew": "\u05E8", + "/reshhebrew": "\u05E8", + "/reshhiriq": "\u05E8", + "/reshhiriqhebrew": "\u05E8", + "/reshholam": "\u05E8", + "/reshholamhebrew": "\u05E8", + "/reshpatah": "\u05E8", + "/reshpatahhebrew": "\u05E8", + "/reshqamats": "\u05E8", + "/reshqamatshebrew": "\u05E8", + "/reshqubuts": "\u05E8", + "/reshqubutshebrew": "\u05E8", + "/reshsegol": "\u05E8", + "/reshsegolhebrew": "\u05E8", + "/reshsheva": "\u05E8", + "/reshshevahebrew": "\u05E8", + "/reshtsere": "\u05E8", + "/reshtserehebrew": "\u05E8", + "/reshwide:hb": "\uFB27", + "/reshwithdagesh:hb": "\uFB48", + "/resourceideographiccircled": "\u32AE", + "/resourceideographicparen": "\u323E", + "/response": "\u211F", + "/restideographiccircled": "\u32A1", + "/restideographicparen": "\u3241", + "/restrictedentryoneleft": "\u26E0", + "/restrictedentrytwoleft": "\u26E1", + "/restroom": "\u1F6BB", + "/return": "\u23CE", + "/reversedHandMiddleFingerExtended": "\u1F595", + "/reversedRaisedHandFingersSplayed": "\u1F591", + "/reversedThumbsDownSign": "\u1F593", + "/reversedThumbsUpSign": "\u1F592", + "/reversedVictoryHand": "\u1F594", + "/reversedonehundred.roman": "\u2183", + "/reversedtilde": "\u223D", + "/reversedzecyr": "\u0511", + "/revia:hb": "\u0597", + "/reviahebrew": "\u0597", + "/reviamugrashhebrew": "\u0597", + "/revlogicalnot": "\u2310", + "/revolvingHearts": "\u1F49E", + "/rfishhook": "\u027E", + "/rfishhookreversed": "\u027F", + "/rgravedbl": "\u0211", + "/rhabengali": "\u09DD", + "/rhacyr": "\u0517", + "/rhadeva": "\u095D", + "/rho": "\u03C1", + "/rhoasper": "\u1FE5", + "/rhofunc": "\u2374", + "/rholenis": "\u1FE4", + "/rhook": "\u027D", + "/rhookturned": "\u027B", + "/rhookturnedsuperior": "\u02B5", + "/rhookturnedsupmod": "\u02B5", + "/rhostrokesymbol": "\u03FC", + "/rhosymbol": "\u03F1", + "/rhosymbolgreek": "\u03F1", + "/rhotichookmod": "\u02DE", + "/rial": "\uFDFC", + "/ribbon": "\u1F380", + "/riceBall": "\u1F359", + "/riceCracker": "\u1F358", + "/ricirclekatakana": "\u32F7", + "/rieulacirclekorean": "\u3271", + "/rieulaparenkorean": "\u3211", + "/rieulcirclekorean": "\u3263", + "/rieulhieuhkorean": "\u3140", + "/rieulkiyeokkorean": "\u313A", + "/rieulkiyeoksioskorean": "\u3169", + "/rieulkorean": "\u3139", + "/rieulmieumkorean": "\u313B", + "/rieulpansioskorean": "\u316C", + "/rieulparenkorean": "\u3203", + "/rieulphieuphkorean": "\u313F", + "/rieulpieupkorean": "\u313C", + "/rieulpieupsioskorean": "\u316B", + "/rieulsioskorean": "\u313D", + "/rieulthieuthkorean": "\u313E", + "/rieultikeutkorean": "\u316A", + "/rieulyeorinhieuhkorean": "\u316D", + "/right-pointingMagnifyingGlass": "\u1F50E", + "/rightAngerBubble": "\u1F5EF", + "/rightHalfBlock": "\u2590", + "/rightHandTelephoneReceiver": "\u1F57D", + "/rightOneEighthBlock": "\u2595", + "/rightSpeaker": "\u1F568", + "/rightSpeakerOneSoundWave": "\u1F569", + "/rightSpeakerThreeSoundWaves": "\u1F56A", + "/rightSpeechBubble": "\u1F5E9", + "/rightThoughtBubble": "\u1F5ED", + "/rightangle": "\u221F", + "/rightarrowoverleftarrow": "\u21C4", + "/rightdnheavyleftuplight": "\u2546", + "/rightharpoonoverleftharpoon": "\u21CC", + "/rightheavyleftdnlight": "\u252E", + "/rightheavyleftuplight": "\u2536", + "/rightheavyleftvertlight": "\u253E", + "/rightideographiccircled": "\u32A8", + "/rightlightleftdnheavy": "\u2531", + "/rightlightleftupheavy": "\u2539", + "/rightlightleftvertheavy": "\u2549", + "/righttackbelowcmb": "\u0319", + "/righttoleftembed": "\u202B", + "/righttoleftisolate": "\u2067", + "/righttoleftmark": "\u200F", + "/righttoleftoverride": "\u202E", + "/righttriangle": "\u22BF", + "/rightupheavyleftdnlight": "\u2544", + "/rihiragana": "\u308A", + "/rikatakana": "\u30EA", + "/rikatakanahalfwidth": "\uFF98", + "/ring": "\u02DA", + "/ringbelowcmb": "\u0325", + "/ringcmb": "\u030A", + "/ringequal": "\u2257", + "/ringhalfleft": "\u02BF", + "/ringhalfleftarmenian": "\u0559", + "/ringhalfleftbelowcmb": "\u031C", + "/ringhalfleftcentered": "\u02D3", + "/ringhalfleftcentredmod": "\u02D3", + "/ringhalfleftmod": "\u02BF", + "/ringhalfright": "\u02BE", + "/ringhalfrightbelowcmb": "\u0339", + "/ringhalfrightcentered": "\u02D2", + "/ringhalfrightcentredmod": "\u02D2", + "/ringhalfrightmod": "\u02BE", + "/ringinequal": "\u2256", + "/ringingBell": "\u1F56D", + "/ringlowmod": "\u02F3", + "/ringoperator": "\u2218", + "/rinsular": "\uA783", + "/rinvertedbreve": "\u0213", + "/rirasquare": "\u3352", + "/risingdiagonal": "\u27CB", + "/rittorusquare": "\u3351", + "/rlinebelow": "\u1E5F", + "/rlongleg": "\u027C", + "/rlonglegturned": "\u027A", + "/rmacrondot": "\u1E5D", + "/rmonospace": "\uFF52", + "/rnoon": "\u06BB", + "/rnoon.fina": "\uFBA1", + "/rnoon.init": "\uFBA2", + "/rnoon.isol": "\uFBA0", + "/rnoon.medi": "\uFBA3", + "/roastedSweetPotato": "\u1F360", + "/robliquestroke": "\uA7A7", + "/rocirclekatakana": "\u32FA", + "/rocket": "\u1F680", + "/rohiragana": "\u308D", + "/rokatakana": "\u30ED", + "/rokatakanahalfwidth": "\uFF9B", + "/rolled-upNewspaper": "\u1F5DE", + "/rollerCoaster": "\u1F3A2", + "/rookblack": "\u265C", + "/rookwhite": "\u2656", + "/rooster": "\u1F413", + "/roruathai": "\u0E23", + "/rose": "\u1F339", + "/rosette": "\u1F3F5", + "/roundPushpin": "\u1F4CD", + "/roundedzeroabove": "\u06DF", + "/rowboat": "\u1F6A3", + "/rparen": "\u24AD", + "/rparenthesized": "\u24AD", + "/rrabengali": "\u09DC", + "/rradeva": "\u0931", + "/rragurmukhi": "\u0A5C", + "/rreh": "\u0691", + "/rreh.fina": "\uFB8D", + "/rreh.isol": "\uFB8C", + "/rreharabic": "\u0691", + "/rrehfinalarabic": "\uFB8D", + "/rrotunda": "\uA75B", + "/rrvocalicbengali": "\u09E0", + "/rrvocalicdeva": "\u0960", + "/rrvocalicgujarati": "\u0AE0", + "/rrvocalicvowelsignbengali": "\u09C4", + "/rrvocalicvowelsigndeva": "\u0944", + "/rrvocalicvowelsigngujarati": "\u0AC4", + "/rstroke": "\u024D", + "/rsuperior": "\uF6F1", + "/rsupmod": "\u02B3", + "/rtailturned": "\u2C79", + "/rtblock": "\u2590", + "/rturned": "\u0279", + "/rturnedsuperior": "\u02B4", + "/rturnedsupmod": "\u02B4", + "/ruble": "\u20BD", + "/rucirclekatakana": "\u32F8", + "/rugbyFootball": "\u1F3C9", + "/ruhiragana": "\u308B", + "/rukatakana": "\u30EB", + "/rukatakanahalfwidth": "\uFF99", + "/rum": "\uA775", + "/rumrotunda": "\uA75D", + "/runner": "\u1F3C3", + "/runningShirtSash": "\u1F3BD", + "/rupeemarkbengali": "\u09F2", + "/rupeesignbengali": "\u09F3", + "/rupiah": "\uF6DD", + "/rupiisquare": "\u3353", + "/ruthai": "\u0E24", + "/ruuburusquare": "\u3354", + "/rvocalicbengali": "\u098B", + "/rvocalicdeva": "\u090B", + "/rvocalicgujarati": "\u0A8B", + "/rvocalicvowelsignbengali": "\u09C3", + "/rvocalicvowelsigndeva": "\u0943", + "/rvocalicvowelsigngujarati": "\u0AC3", + "/s": "\u0073", + "/s.inferior": "\u209B", + "/s_t": "\uFB06", + "/sabengali": "\u09B8", + "/sacirclekatakana": "\u32DA", + "/sacute": "\u015B", + "/sacutedotaccent": "\u1E65", + "/sad": "\u0635", + "/sad.fina": "\uFEBA", + "/sad.init": "\uFEBB", + "/sad.init_alefmaksura.fina": "\uFD05", + "/sad.init_hah.fina": "\uFC20", + "/sad.init_hah.medi": "\uFCB1", + "/sad.init_hah.medi_hah.medi": "\uFD65", + "/sad.init_khah.medi": "\uFCB2", + "/sad.init_meem.fina": "\uFC21", + "/sad.init_meem.medi": "\uFCB3", + "/sad.init_meem.medi_meem.medi": "\uFDC5", + "/sad.init_reh.fina": "\uFD0F", + "/sad.init_yeh.fina": "\uFD06", + "/sad.isol": "\uFEB9", + "/sad.medi": "\uFEBC", + "/sad.medi_alefmaksura.fina": "\uFD21", + "/sad.medi_hah.medi_hah.fina": "\uFD64", + "/sad.medi_hah.medi_yeh.fina": "\uFDA9", + "/sad.medi_meem.medi_meem.fina": "\uFD66", + "/sad.medi_reh.fina": "\uFD2B", + "/sad.medi_yeh.fina": "\uFD22", + "/sad_lam_alefmaksuraabove": "\u06D6", + "/sadarabic": "\u0635", + "/sadeva": "\u0938", + "/sadfinalarabic": "\uFEBA", + "/sadinitialarabic": "\uFEBB", + "/sadmedialarabic": "\uFEBC", + "/sadthreedotsabove": "\u069E", + "/sadtwodotsbelow": "\u069D", + "/sagittarius": "\u2650", + "/sagujarati": "\u0AB8", + "/sagurmukhi": "\u0A38", + "/sahiragana": "\u3055", + "/saikurusquare": "\u331F", + "/sailboat": "\u26F5", + "/sakatakana": "\u30B5", + "/sakatakanahalfwidth": "\uFF7B", + "/sakeBottleAndCup": "\u1F376", + "/sallallahoualayhewasallamarabic": "\uFDFA", + "/saltillo": "\uA78C", + "/saltire": "\u2613", + "/samahaprana": "\uA9B0", + "/samekh": "\u05E1", + "/samekh:hb": "\u05E1", + "/samekhdagesh": "\uFB41", + "/samekhdageshhebrew": "\uFB41", + "/samekhhebrew": "\u05E1", + "/samekhwithdagesh:hb": "\uFB41", + "/sampi": "\u03E1", + "/sampiarchaic": "\u0373", + "/samurda": "\uA9AF", + "/samvat": "\u0604", + "/san": "\u03FB", + "/santiimusquare": "\u3320", + "/saraaathai": "\u0E32", + "/saraaethai": "\u0E41", + "/saraaimaimalaithai": "\u0E44", + "/saraaimaimuanthai": "\u0E43", + "/saraamthai": "\u0E33", + "/saraathai": "\u0E30", + "/saraethai": "\u0E40", + "/saraiileftthai": "\uF886", + "/saraiithai": "\u0E35", + "/saraileftthai": "\uF885", + "/saraithai": "\u0E34", + "/saraothai": "\u0E42", + "/saraueeleftthai": "\uF888", + "/saraueethai": "\u0E37", + "/saraueleftthai": "\uF887", + "/sarauethai": "\u0E36", + "/sarauthai": "\u0E38", + "/sarauuthai": "\u0E39", + "/satellite": "\u1F6F0", + "/satelliteAntenna": "\u1F4E1", + "/saturn": "\u2644", + "/saxophone": "\u1F3B7", + "/sbopomofo": "\u3119", + "/scales": "\u2696", + "/scanninehorizontal": "\u23BD", + "/scanonehorizontal": "\u23BA", + "/scansevenhorizontal": "\u23BC", + "/scanthreehorizontal": "\u23BB", + "/scaron": "\u0161", + "/scarondot": "\u1E67", + "/scarondotaccent": "\u1E67", + "/scedilla": "\u015F", + "/school": "\u1F3EB", + "/schoolSatchel": "\u1F392", + "/schoolideographiccircled": "\u3246", + "/schwa": "\u0259", + "/schwa.inferior": "\u2094", + "/schwacyr": "\u04D9", + "/schwacyrillic": "\u04D9", + "/schwadieresiscyr": "\u04DB", + "/schwadieresiscyrillic": "\u04DB", + "/schwahook": "\u025A", + "/scircle": "\u24E2", + "/scircumflex": "\u015D", + "/scommaaccent": "\u0219", + "/scooter": "\u1F6F4", + "/scorpius": "\u264F", + "/screen": "\u1F5B5", + "/scroll": "\u1F4DC", + "/scruple": "\u2108", + "/sdot": "\u1E61", + "/sdotaccent": "\u1E61", + "/sdotbelow": "\u1E63", + "/sdotbelowdotabove": "\u1E69", + "/sdotbelowdotaccent": "\u1E69", + "/seagullbelowcmb": "\u033C", + "/seat": "\u1F4BA", + "/secirclekatakana": "\u32DD", + "/second": "\u2033", + "/secondreversed": "\u2036", + "/secondscreensquare": "\u1F19C", + "/secondtonechinese": "\u02CA", + "/secretideographiccircled": "\u3299", + "/section": "\u00A7", + "/sectionsignhalftop": "\u2E39", + "/sector": "\u2314", + "/seeNoEvilMonkey": "\u1F648", + "/seedling": "\u1F331", + "/seen": "\u0633", + "/seen.fina": "\uFEB2", + "/seen.init": "\uFEB3", + "/seen.init_alefmaksura.fina": "\uFCFB", + "/seen.init_hah.fina": "\uFC1D", + "/seen.init_hah.medi": "\uFCAE", + "/seen.init_hah.medi_jeem.medi": "\uFD5C", + "/seen.init_heh.medi": "\uFD31", + "/seen.init_jeem.fina": "\uFC1C", + "/seen.init_jeem.medi": "\uFCAD", + "/seen.init_jeem.medi_hah.medi": "\uFD5D", + "/seen.init_khah.fina": "\uFC1E", + "/seen.init_khah.medi": "\uFCAF", + "/seen.init_meem.fina": "\uFC1F", + "/seen.init_meem.medi": "\uFCB0", + "/seen.init_meem.medi_hah.medi": "\uFD60", + "/seen.init_meem.medi_jeem.medi": "\uFD61", + "/seen.init_meem.medi_meem.medi": "\uFD63", + "/seen.init_reh.fina": "\uFD0E", + "/seen.init_yeh.fina": "\uFCFC", + "/seen.isol": "\uFEB1", + "/seen.medi": "\uFEB4", + "/seen.medi_alefmaksura.fina": "\uFD17", + "/seen.medi_hah.medi": "\uFD35", + "/seen.medi_heh.medi": "\uFCE8", + "/seen.medi_jeem.medi": "\uFD34", + "/seen.medi_jeem.medi_alefmaksura.fina": "\uFD5E", + "/seen.medi_khah.medi": "\uFD36", + "/seen.medi_khah.medi_alefmaksura.fina": "\uFDA8", + "/seen.medi_khah.medi_yeh.fina": "\uFDC6", + "/seen.medi_meem.medi": "\uFCE7", + "/seen.medi_meem.medi_hah.fina": "\uFD5F", + "/seen.medi_meem.medi_meem.fina": "\uFD62", + "/seen.medi_reh.fina": "\uFD2A", + "/seen.medi_yeh.fina": "\uFD18", + "/seenDigitFourAbove": "\u077D", + "/seenFourDotsAbove": "\u075C", + "/seenInvertedV": "\u077E", + "/seenSmallTahTwoDots": "\u0770", + "/seenTwoDotsVerticallyAbove": "\u076D", + "/seenabove": "\u06DC", + "/seenarabic": "\u0633", + "/seendotbelowdotabove": "\u069A", + "/seenfinalarabic": "\uFEB2", + "/seeninitialarabic": "\uFEB3", + "/seenlow": "\u06E3", + "/seenmedialarabic": "\uFEB4", + "/seenthreedotsbelow": "\u069B", + "/seenthreedotsbelowthreedotsabove": "\u069C", + "/segment": "\u2313", + "/segol": "\u05B6", + "/segol13": "\u05B6", + "/segol1f": "\u05B6", + "/segol2c": "\u05B6", + "/segol:hb": "\u05B6", + "/segolhebrew": "\u05B6", + "/segolnarrowhebrew": "\u05B6", + "/segolquarterhebrew": "\u05B6", + "/segolta:hb": "\u0592", + "/segoltahebrew": "\u0592", + "/segolwidehebrew": "\u05B6", + "/seharmenian": "\u057D", + "/sehiragana": "\u305B", + "/sekatakana": "\u30BB", + "/sekatakanahalfwidth": "\uFF7E", + "/selfideographicparen": "\u3242", + "/semicolon": "\u003B", + "/semicolonarabic": "\u061B", + "/semicolonmonospace": "\uFF1B", + "/semicolonreversed": "\u204F", + "/semicolonsmall": "\uFE54", + "/semicolonunderlinefunc": "\u236E", + "/semidirectproductleft": "\u22CB", + "/semidirectproductright": "\u22CC", + "/semisextile": "\u26BA", + "/semisoftcyr": "\u048D", + "/semivoicedmarkkana": "\u309C", + "/semivoicedmarkkanahalfwidth": "\uFF9F", + "/sentisquare": "\u3322", + "/sentosquare": "\u3323", + "/septembertelegraph": "\u32C8", + "/sersetdblup": "\u22D1", + "/sersetnotequalup": "\u228B", + "/servicemark": "\u2120", + "/sesamedot": "\uFE45", + "/sesquiquadrate": "\u26BC", + "/setminus": "\u2216", + "/seven": "\u0037", + "/seven.inferior": "\u2087", + "/seven.roman": "\u2166", + "/seven.romansmall": "\u2176", + "/seven.superior": "\u2077", + "/sevenarabic": "\u0667", + "/sevenbengali": "\u09ED", + "/sevencircle": "\u2466", + "/sevencircledbl": "\u24FB", + "/sevencircleinversesansserif": "\u2790", + "/sevencomma": "\u1F108", + "/sevendeva": "\u096D", + "/seveneighths": "\u215E", + "/sevenfar": "\u06F7", + "/sevengujarati": "\u0AED", + "/sevengurmukhi": "\u0A6D", + "/sevenhackarabic": "\u0667", + "/sevenhangzhou": "\u3027", + "/sevenideographiccircled": "\u3286", + "/sevenideographicparen": "\u3226", + "/seveninferior": "\u2087", + "/sevenmonospace": "\uFF17", + "/sevenoldstyle": "\uF737", + "/sevenparen": "\u247A", + "/sevenparenthesized": "\u247A", + "/sevenperiod": "\u248E", + "/sevenpersian": "\u06F7", + "/sevenpointonesquare": "\u1F1A1", + "/sevenroman": "\u2176", + "/sevensuperior": "\u2077", + "/seventeencircle": "\u2470", + "/seventeencircleblack": "\u24F1", + "/seventeenparen": "\u2484", + "/seventeenparenthesized": "\u2484", + "/seventeenperiod": "\u2498", + "/seventhai": "\u0E57", + "/seventycirclesquare": "\u324E", + "/sextile": "\u26B9", + "/sfthyphen": "\u00AD", + "/shaarmenian": "\u0577", + "/shabengali": "\u09B6", + "/shacyr": "\u0448", + "/shacyrillic": "\u0448", + "/shaddaAlefIsol": "\uFC63", + "/shaddaDammaIsol": "\uFC61", + "/shaddaDammaMedi": "\uFCF3", + "/shaddaDammatanIsol": "\uFC5E", + "/shaddaFathaIsol": "\uFC60", + "/shaddaFathaMedi": "\uFCF2", + "/shaddaIsol": "\uFE7C", + "/shaddaKasraIsol": "\uFC62", + "/shaddaKasraMedi": "\uFCF4", + "/shaddaKasratanIsol": "\uFC5F", + "/shaddaMedi": "\uFE7D", + "/shaddaarabic": "\u0651", + "/shaddadammaarabic": "\uFC61", + "/shaddadammatanarabic": "\uFC5E", + "/shaddafathaarabic": "\uFC60", + "/shaddafathatanarabic": "\u0651", + "/shaddakasraarabic": "\uFC62", + "/shaddakasratanarabic": "\uFC5F", + "/shade": "\u2592", + "/shadedark": "\u2593", + "/shadelight": "\u2591", + "/shademedium": "\u2592", + "/shadeva": "\u0936", + "/shagujarati": "\u0AB6", + "/shagurmukhi": "\u0A36", + "/shalshelet:hb": "\u0593", + "/shalshelethebrew": "\u0593", + "/shamrock": "\u2618", + "/shavedIce": "\u1F367", + "/shbopomofo": "\u3115", + "/shchacyr": "\u0449", + "/shchacyrillic": "\u0449", + "/sheen": "\u0634", + "/sheen.fina": "\uFEB6", + "/sheen.init": "\uFEB7", + "/sheen.init_alefmaksura.fina": "\uFCFD", + "/sheen.init_hah.fina": "\uFD0A", + "/sheen.init_hah.medi": "\uFD2E", + "/sheen.init_hah.medi_meem.medi": "\uFD68", + "/sheen.init_heh.medi": "\uFD32", + "/sheen.init_jeem.fina": "\uFD09", + "/sheen.init_jeem.medi": "\uFD2D", + "/sheen.init_khah.fina": "\uFD0B", + "/sheen.init_khah.medi": "\uFD2F", + "/sheen.init_meem.fina": "\uFD0C", + "/sheen.init_meem.medi": "\uFD30", + "/sheen.init_meem.medi_khah.medi": "\uFD6B", + "/sheen.init_meem.medi_meem.medi": "\uFD6D", + "/sheen.init_reh.fina": "\uFD0D", + "/sheen.init_yeh.fina": "\uFCFE", + "/sheen.isol": "\uFEB5", + "/sheen.medi": "\uFEB8", + "/sheen.medi_alefmaksura.fina": "\uFD19", + "/sheen.medi_hah.fina": "\uFD26", + "/sheen.medi_hah.medi": "\uFD38", + "/sheen.medi_hah.medi_meem.fina": "\uFD67", + "/sheen.medi_hah.medi_yeh.fina": "\uFDAA", + "/sheen.medi_heh.medi": "\uFCEA", + "/sheen.medi_jeem.fina": "\uFD25", + "/sheen.medi_jeem.medi": "\uFD37", + "/sheen.medi_jeem.medi_yeh.fina": "\uFD69", + "/sheen.medi_khah.fina": "\uFD27", + "/sheen.medi_khah.medi": "\uFD39", + "/sheen.medi_meem.fina": "\uFD28", + "/sheen.medi_meem.medi": "\uFCE9", + "/sheen.medi_meem.medi_khah.fina": "\uFD6A", + "/sheen.medi_meem.medi_meem.fina": "\uFD6C", + "/sheen.medi_reh.fina": "\uFD29", + "/sheen.medi_yeh.fina": "\uFD1A", + "/sheenarabic": "\u0634", + "/sheendotbelow": "\u06FA", + "/sheenfinalarabic": "\uFEB6", + "/sheeninitialarabic": "\uFEB7", + "/sheenmedialarabic": "\uFEB8", + "/sheep": "\u1F411", + "/sheicoptic": "\u03E3", + "/shelfmod": "\u02FD", + "/shelfopenmod": "\u02FE", + "/sheqel": "\u20AA", + "/sheqelhebrew": "\u20AA", + "/sheva": "\u05B0", + "/sheva115": "\u05B0", + "/sheva15": "\u05B0", + "/sheva22": "\u05B0", + "/sheva2e": "\u05B0", + "/sheva:hb": "\u05B0", + "/shevahebrew": "\u05B0", + "/shevanarrowhebrew": "\u05B0", + "/shevaquarterhebrew": "\u05B0", + "/shevawidehebrew": "\u05B0", + "/shhacyr": "\u04BB", + "/shhacyrillic": "\u04BB", + "/shhatailcyr": "\u0527", + "/shield": "\u1F6E1", + "/shimacoptic": "\u03ED", + "/shin": "\u05E9", + "/shin:hb": "\u05E9", + "/shinDot:hb": "\u05C1", + "/shindagesh": "\uFB49", + "/shindageshhebrew": "\uFB49", + "/shindageshshindot": "\uFB2C", + "/shindageshshindothebrew": "\uFB2C", + "/shindageshsindot": "\uFB2D", + "/shindageshsindothebrew": "\uFB2D", + "/shindothebrew": "\u05C1", + "/shinhebrew": "\u05E9", + "/shinshindot": "\uFB2A", + "/shinshindothebrew": "\uFB2A", + "/shinsindot": "\uFB2B", + "/shinsindothebrew": "\uFB2B", + "/shintoshrine": "\u26E9", + "/shinwithdagesh:hb": "\uFB49", + "/shinwithdageshandshinDot:hb": "\uFB2C", + "/shinwithdageshandsinDot:hb": "\uFB2D", + "/shinwithshinDot:hb": "\uFB2A", + "/shinwithsinDot:hb": "\uFB2B", + "/ship": "\u1F6A2", + "/sho": "\u03F8", + "/shoejotupfunc": "\u235D", + "/shoestiledownfunc": "\u2366", + "/shoestileleftfunc": "\u2367", + "/shogipieceblack": "\u2617", + "/shogipiecewhite": "\u2616", + "/shook": "\u0282", + "/shootingStar": "\u1F320", + "/shoppingBags": "\u1F6CD", + "/shoppingTrolley": "\u1F6D2", + "/shortcake": "\u1F370", + "/shortequalsmod": "\uA78A", + "/shortoverlongmetrical": "\u23D3", + "/shoulderedopenbox": "\u237D", + "/shower": "\u1F6BF", + "/shvsquare": "\u1F1AA", + "/sicirclekatakana": "\u32DB", + "/sidewaysBlackDownPointingIndex": "\u1F5A1", + "/sidewaysBlackLeftPointingIndex": "\u1F59A", + "/sidewaysBlackRightPointingIndex": "\u1F59B", + "/sidewaysBlackUpPointingIndex": "\u1F5A0", + "/sidewaysWhiteDownPointingIndex": "\u1F59F", + "/sidewaysWhiteLeftPointingIndex": "\u1F598", + "/sidewaysWhiteRightPointingIndex": "\u1F599", + "/sidewaysWhiteUpPointingIndex": "\u1F59E", + "/sigma": "\u03C3", + "/sigma1": "\u03C2", + "/sigmafinal": "\u03C2", + "/sigmalunatedottedreversedsymbol": "\u037D", + "/sigmalunatedottedsymbol": "\u037C", + "/sigmalunatereversedsymbol": "\u037B", + "/sigmalunatesymbol": "\u03F2", + "/sigmalunatesymbolgreek": "\u03F2", + "/sihiragana": "\u3057", + "/sikatakana": "\u30B7", + "/sikatakanahalfwidth": "\uFF7C", + "/silhouetteOfJapan": "\u1F5FE", + "/siluqhebrew": "\u05BD", + "/siluqlefthebrew": "\u05BD", + "/similar": "\u223C", + "/sinDot:hb": "\u05C2", + "/sindothebrew": "\u05C2", + "/sinewave": "\u223F", + "/sinh:a": "\u0D85", + "/sinh:aa": "\u0D86", + "/sinh:aae": "\u0D88", + "/sinh:aaesign": "\u0DD1", + "/sinh:aasign": "\u0DCF", + "/sinh:ae": "\u0D87", + "/sinh:aesign": "\u0DD0", + "/sinh:ai": "\u0D93", + "/sinh:aisign": "\u0DDB", + "/sinh:anusvara": "\u0D82", + "/sinh:au": "\u0D96", + "/sinh:ausign": "\u0DDE", + "/sinh:ba": "\u0DB6", + "/sinh:bha": "\u0DB7", + "/sinh:ca": "\u0DA0", + "/sinh:cha": "\u0DA1", + "/sinh:da": "\u0DAF", + "/sinh:dda": "\u0DA9", + "/sinh:ddha": "\u0DAA", + "/sinh:dha": "\u0DB0", + "/sinh:e": "\u0D91", + "/sinh:ee": "\u0D92", + "/sinh:eesign": "\u0DDA", + "/sinh:esign": "\u0DD9", + "/sinh:fa": "\u0DC6", + "/sinh:ga": "\u0D9C", + "/sinh:gha": "\u0D9D", + "/sinh:ha": "\u0DC4", + "/sinh:i": "\u0D89", + "/sinh:ii": "\u0D8A", + "/sinh:iisign": "\u0DD3", + "/sinh:isign": "\u0DD2", + "/sinh:ja": "\u0DA2", + "/sinh:jha": "\u0DA3", + "/sinh:jnya": "\u0DA5", + "/sinh:ka": "\u0D9A", + "/sinh:kha": "\u0D9B", + "/sinh:kunddaliya": "\u0DF4", + "/sinh:la": "\u0DBD", + "/sinh:litheight": "\u0DEE", + "/sinh:lithfive": "\u0DEB", + "/sinh:lithfour": "\u0DEA", + "/sinh:lithnine": "\u0DEF", + "/sinh:lithone": "\u0DE7", + "/sinh:lithseven": "\u0DED", + "/sinh:lithsix": "\u0DEC", + "/sinh:liththree": "\u0DE9", + "/sinh:lithtwo": "\u0DE8", + "/sinh:lithzero": "\u0DE6", + "/sinh:lla": "\u0DC5", + "/sinh:llvocal": "\u0D90", + "/sinh:llvocalsign": "\u0DF3", + "/sinh:lvocal": "\u0D8F", + "/sinh:lvocalsign": "\u0DDF", + "/sinh:ma": "\u0DB8", + "/sinh:mba": "\u0DB9", + "/sinh:na": "\u0DB1", + "/sinh:nda": "\u0DB3", + "/sinh:nga": "\u0D9E", + "/sinh:nna": "\u0DAB", + "/sinh:nndda": "\u0DAC", + "/sinh:nnga": "\u0D9F", + "/sinh:nya": "\u0DA4", + "/sinh:nyja": "\u0DA6", + "/sinh:o": "\u0D94", + "/sinh:oo": "\u0D95", + "/sinh:oosign": "\u0DDD", + "/sinh:osign": "\u0DDC", + "/sinh:pa": "\u0DB4", + "/sinh:pha": "\u0DB5", + "/sinh:ra": "\u0DBB", + "/sinh:rrvocal": "\u0D8E", + "/sinh:rrvocalsign": "\u0DF2", + "/sinh:rvocal": "\u0D8D", + "/sinh:rvocalsign": "\u0DD8", + "/sinh:sa": "\u0DC3", + "/sinh:sha": "\u0DC1", + "/sinh:ssa": "\u0DC2", + "/sinh:ta": "\u0DAD", + "/sinh:tha": "\u0DAE", + "/sinh:tta": "\u0DA7", + "/sinh:ttha": "\u0DA8", + "/sinh:u": "\u0D8B", + "/sinh:usign": "\u0DD4", + "/sinh:uu": "\u0D8C", + "/sinh:uusign": "\u0DD6", + "/sinh:va": "\u0DC0", + "/sinh:virama": "\u0DCA", + "/sinh:visarga": "\u0D83", + "/sinh:ya": "\u0DBA", + "/sinologicaldot": "\uA78F", + "/sinsular": "\uA785", + "/siosacirclekorean": "\u3274", + "/siosaparenkorean": "\u3214", + "/sioscieuckorean": "\u317E", + "/sioscirclekorean": "\u3266", + "/sioskiyeokkorean": "\u317A", + "/sioskorean": "\u3145", + "/siosnieunkorean": "\u317B", + "/siosparenkorean": "\u3206", + "/siospieupkorean": "\u317D", + "/siostikeutkorean": "\u317C", + "/siringusquare": "\u3321", + "/six": "\u0036", + "/six.inferior": "\u2086", + "/six.roman": "\u2165", + "/six.romansmall": "\u2175", + "/six.superior": "\u2076", + "/sixPointedStarMiddleDot": "\u1F52F", + "/sixarabic": "\u0666", + "/sixbengali": "\u09EC", + "/sixcircle": "\u2465", + "/sixcircledbl": "\u24FA", + "/sixcircleinversesansserif": "\u278F", + "/sixcomma": "\u1F107", + "/sixdeva": "\u096C", + "/sixdotsvertical": "\u2E3D", + "/sixfar": "\u06F6", + "/sixgujarati": "\u0AEC", + "/sixgurmukhi": "\u0A6C", + "/sixhackarabic": "\u0666", + "/sixhangzhou": "\u3026", + "/sixideographiccircled": "\u3285", + "/sixideographicparen": "\u3225", + "/sixinferior": "\u2086", + "/sixlateform.roman": "\u2185", + "/sixmonospace": "\uFF16", + "/sixoldstyle": "\uF736", + "/sixparen": "\u2479", + "/sixparenthesized": "\u2479", + "/sixperemspace": "\u2006", + "/sixperiod": "\u248D", + "/sixpersian": "\u06F6", + "/sixroman": "\u2175", + "/sixsuperior": "\u2076", + "/sixteencircle": "\u246F", + "/sixteencircleblack": "\u24F0", + "/sixteencurrencydenominatorbengali": "\u09F9", + "/sixteenparen": "\u2483", + "/sixteenparenthesized": "\u2483", + "/sixteenperiod": "\u2497", + "/sixthai": "\u0E56", + "/sixtycirclesquare": "\u324D", + "/sixtypsquare": "\u1F1A3", + "/sjekomicyr": "\u050D", + "/skiAndSkiBoot": "\u1F3BF", + "/skier": "\u26F7", + "/skull": "\u1F480", + "/skullcrossbones": "\u2620", + "/slash": "\u002F", + "/slashbarfunc": "\u233F", + "/slashmonospace": "\uFF0F", + "/sled": "\u1F6F7", + "/sleeping": "\u1F4A4", + "/sleepingAccommodation": "\u1F6CC", + "/sleepingFace": "\u1F634", + "/sleepyFace": "\u1F62A", + "/sleuthOrSpy": "\u1F575", + "/sliceOfPizza": "\u1F355", + "/slightlyFrowningFace": "\u1F641", + "/slightlySmilingFace": "\u1F642", + "/slong": "\u017F", + "/slongdotaccent": "\u1E9B", + "/slope": "\u2333", + "/slotMachine": "\u1F3B0", + "/smallAirplane": "\u1F6E9", + "/smallBlueDiamond": "\u1F539", + "/smallOrangeDiamond": "\u1F538", + "/smallRedTriangleDOwn": "\u1F53D", + "/smallRedTriangleUp": "\u1F53C", + "/smile": "\u2323", + "/smileface": "\u263A", + "/smilingCatFaceWithHeartShapedEyes": "\u1F63B", + "/smilingCatFaceWithOpenMouth": "\u1F63A", + "/smilingFaceWithHalo": "\u1F607", + "/smilingFaceWithHeartShapedEyes": "\u1F60D", + "/smilingFaceWithHorns": "\u1F608", + "/smilingFaceWithOpenMouth": "\u1F603", + "/smilingFaceWithOpenMouthAndColdSweat": "\u1F605", + "/smilingFaceWithOpenMouthAndSmilingEyes": "\u1F604", + "/smilingFaceWithOpenMouthAndTightlyClosedEyes": "\u1F606", + "/smilingFaceWithSmilingEyes": "\u1F60A", + "/smilingFaceWithSunglasses": "\u1F60E", + "/smilingfaceblack": "\u263B", + "/smilingfacewhite": "\u263A", + "/smirkingFace": "\u1F60F", + "/smll:ampersand": "\uFE60", + "/smll:asterisk": "\uFE61", + "/smll:backslash": "\uFE68", + "/smll:braceleft": "\uFE5B", + "/smll:braceright": "\uFE5C", + "/smll:colon": "\uFE55", + "/smll:comma": "\uFE50", + "/smll:dollar": "\uFE69", + "/smll:emdash": "\uFE58", + "/smll:equal": "\uFE66", + "/smll:exclam": "\uFE57", + "/smll:greater": "\uFE65", + "/smll:hyphen": "\uFE63", + "/smll:ideographiccomma": "\uFE51", + "/smll:less": "\uFE64", + "/smll:numbersign": "\uFE5F", + "/smll:parenthesisleft": "\uFE59", + "/smll:parenthesisright": "\uFE5A", + "/smll:percent": "\uFE6A", + "/smll:period": "\uFE52", + "/smll:plus": "\uFE62", + "/smll:question": "\uFE56", + "/smll:semicolon": "\uFE54", + "/smll:tortoiseshellbracketleft": "\uFE5D", + "/smll:tortoiseshellbracketright": "\uFE5E", + "/smoking": "\u1F6AC", + "/smonospace": "\uFF53", + "/snail": "\u1F40C", + "/snake": "\u1F40D", + "/snowboarder": "\u1F3C2", + "/snowcappedMountain": "\u1F3D4", + "/snowman": "\u2603", + "/snowmanblack": "\u26C7", + "/snowmanoutsnow": "\u26C4", + "/sobliquestroke": "\uA7A9", + "/soccerball": "\u26BD", + "/societyideographiccircled": "\u3293", + "/societyideographicparen": "\u3233", + "/socirclekatakana": "\u32DE", + "/sofPasuq:hb": "\u05C3", + "/sofpasuqhebrew": "\u05C3", + "/softIceCream": "\u1F366", + "/softShellFloppyDisk": "\u1F5AC", + "/softcyr": "\u044C", + "/softhyphen": "\u00AD", + "/softsigncyrillic": "\u044C", + "/softwarefunction": "\u2394", + "/sohiragana": "\u305D", + "/sokatakana": "\u30BD", + "/sokatakanahalfwidth": "\uFF7F", + "/soliduslongoverlaycmb": "\u0338", + "/solidusshortoverlaycmb": "\u0337", + "/solidussubsetreversepreceding": "\u27C8", + "/solidussupersetpreceding": "\u27C9", + "/soonRightwardsArrowAbove": "\u1F51C", + "/sorusithai": "\u0E29", + "/sosalathai": "\u0E28", + "/sosothai": "\u0E0B", + "/sossquare": "\u1F198", + "/sosuathai": "\u0E2A", + "/soundcopyright": "\u2117", + "/space": "\u0020", + "/spacehackarabic": "\u0020", + "/spade": "\u2660", + "/spadeblack": "\u2660", + "/spadesuitblack": "\u2660", + "/spadesuitwhite": "\u2664", + "/spadewhite": "\u2664", + "/spaghetti": "\u1F35D", + "/sparen": "\u24AE", + "/sparenthesized": "\u24AE", + "/sparklingHeart": "\u1F496", + "/speakNoEvilMonkey": "\u1F64A", + "/speaker": "\u1F508", + "/speakerCancellationStroke": "\u1F507", + "/speakerOneSoundWave": "\u1F509", + "/speakerThreeSoundWaves": "\u1F50A", + "/speakingHeadInSilhouette": "\u1F5E3", + "/specialideographiccircled": "\u3295", + "/specialideographicparen": "\u3235", + "/speechBalloon": "\u1F4AC", + "/speedboat": "\u1F6A4", + "/spesmilo": "\u20B7", + "/sphericalangle": "\u2222", + "/spider": "\u1F577", + "/spiderWeb": "\u1F578", + "/spiralCalendarPad": "\u1F5D3", + "/spiralNotePad": "\u1F5D2", + "/spiralShell": "\u1F41A", + "/splashingSweat": "\u1F4A6", + "/sportsMedal": "\u1F3C5", + "/spoutingWhale": "\u1F433", + "/sppl:tildevertical": "\u2E2F", + "/squarebelowcmb": "\u033B", + "/squareblack": "\u25A0", + "/squarebracketleftvertical": "\uFE47", + "/squarebracketrightvertical": "\uFE48", + "/squarecap": "\u2293", + "/squarecc": "\u33C4", + "/squarecm": "\u339D", + "/squarecup": "\u2294", + "/squareddotoperator": "\u22A1", + "/squarediagonalcrosshatchfill": "\u25A9", + "/squaredj": "\u1F190", + "/squaredkey": "\u26BF", + "/squaredminus": "\u229F", + "/squaredplus": "\u229E", + "/squaredsaltire": "\u26DD", + "/squaredtimes": "\u22A0", + "/squarefourcorners": "\u26F6", + "/squarehalfleftblack": "\u25E7", + "/squarehalfrightblack": "\u25E8", + "/squarehorizontalfill": "\u25A4", + "/squareimage": "\u228F", + "/squareimageorequal": "\u2291", + "/squareimageornotequal": "\u22E4", + "/squarekg": "\u338F", + "/squarekm": "\u339E", + "/squarekmcapital": "\u33CE", + "/squareln": "\u33D1", + "/squarelog": "\u33D2", + "/squarelowerdiagonalhalfrightblack": "\u25EA", + "/squaremediumblack": "\u25FC", + "/squaremediumwhite": "\u25FB", + "/squaremg": "\u338E", + "/squaremil": "\u33D5", + "/squaremm": "\u339C", + "/squaremsquared": "\u33A1", + "/squareoriginal": "\u2290", + "/squareoriginalorequal": "\u2292", + "/squareoriginalornotequal": "\u22E5", + "/squareorthogonalcrosshatchfill": "\u25A6", + "/squareraised": "\u2E0B", + "/squaresmallblack": "\u25AA", + "/squaresmallmediumblack": "\u25FE", + "/squaresmallmediumwhite": "\u25FD", + "/squaresmallwhite": "\u25AB", + "/squareupperdiagonalhalfleftblack": "\u25E9", + "/squareupperlefttolowerrightfill": "\u25A7", + "/squareupperrighttolowerleftfill": "\u25A8", + "/squareverticalfill": "\u25A5", + "/squarewhite": "\u25A1", + "/squarewhitebisectinglinevertical": "\u25EB", + "/squarewhitelowerquadrantleft": "\u25F1", + "/squarewhitelowerquadrantright": "\u25F2", + "/squarewhiteround": "\u25A2", + "/squarewhiteupperquadrantleft": "\u25F0", + "/squarewhiteupperquadrantright": "\u25F3", + "/squarewhitewithsmallblack": "\u25A3", + "/squarewhitewithsquaresmallblack": "\u25A3", + "/squishquadfunc": "\u2337", + "/srfullwidth": "\u33DB", + "/srsquare": "\u33DB", + "/ssabengali": "\u09B7", + "/ssadeva": "\u0937", + "/ssagujarati": "\u0AB7", + "/ssangcieuckorean": "\u3149", + "/ssanghieuhkorean": "\u3185", + "/ssangieungkorean": "\u3180", + "/ssangkiyeokkorean": "\u3132", + "/ssangnieunkorean": "\u3165", + "/ssangpieupkorean": "\u3143", + "/ssangsioskorean": "\u3146", + "/ssangtikeutkorean": "\u3138", + "/ssuperior": "\uF6F2", + "/ssupmod": "\u02E2", + "/sswashtail": "\u023F", + "/stackedcommadbl": "\u2E49", + "/stadium": "\u1F3DF", + "/staffofaesculapius": "\u2695", + "/staffofhermes": "\u269A", + "/stampedEnvelope": "\u1F583", + "/star": "\u22C6", + "/starblack": "\u2605", + "/starcrescent": "\u262A", + "/stardiaeresisfunc": "\u2363", + "/starequals": "\u225B", + "/staroperator": "\u22C6", + "/staroutlinedwhite": "\u269D", + "/starwhite": "\u2606", + "/station": "\u1F689", + "/statueOfLiberty": "\u1F5FD", + "/steamLocomotive": "\u1F682", + "/steamingBowl": "\u1F35C", + "/stenographicfullstop": "\u2E3C", + "/sterling": "\u00A3", + "/sterlingmonospace": "\uFFE1", + "/stigma": "\u03DB", + "/stiletildefunc": "\u236D", + "/stockChart": "\u1F5E0", + "/stockideographiccircled": "\u3291", + "/stockideographicparen": "\u3231", + "/stopabove": "\u06EB", + "/stopbelow": "\u06EA", + "/straightRuler": "\u1F4CF", + "/straightness": "\u23E4", + "/strawberry": "\u1F353", + "/stresslowtonemod": "\uA721", + "/stresstonemod": "\uA720", + "/strictlyequivalent": "\u2263", + "/strokelongoverlaycmb": "\u0336", + "/strokeshortoverlaycmb": "\u0335", + "/studioMicrophone": "\u1F399", + "/studyideographiccircled": "\u32AB", + "/studyideographicparen": "\u323B", + "/stupa": "\u1F6D3", + "/subscriptalef": "\u0656", + "/subset": "\u2282", + "/subsetdbl": "\u22D0", + "/subsetnotequal": "\u228A", + "/subsetorequal": "\u2286", + "/succeeds": "\u227B", + "/succeedsbutnotequivalent": "\u22E9", + "/succeedsorequal": "\u227D", + "/succeedsorequivalent": "\u227F", + "/succeedsunderrelation": "\u22B1", + "/suchthat": "\u220B", + "/sucirclekatakana": "\u32DC", + "/suhiragana": "\u3059", + "/suitableideographiccircled": "\u329C", + "/sukatakana": "\u30B9", + "/sukatakanahalfwidth": "\uFF7D", + "/sukumendutvowel": "\uA9B9", + "/sukunIsol": "\uFE7E", + "/sukunMedi": "\uFE7F", + "/sukunarabic": "\u0652", + "/sukuvowel": "\uA9B8", + "/summation": "\u2211", + "/summationbottom": "\u23B3", + "/summationdblstruck": "\u2140", + "/summationtop": "\u23B2", + "/sun": "\u263C", + "/sunFace": "\u1F31E", + "/sunbehindcloud": "\u26C5", + "/sunflower": "\u1F33B", + "/sunideographiccircled": "\u3290", + "/sunideographicparen": "\u3230", + "/sunraysblack": "\u2600", + "/sunrayswhite": "\u263C", + "/sunrise": "\u1F305", + "/sunriseOverMountains": "\u1F304", + "/sunsetOverBuildings": "\u1F307", + "/superset": "\u2283", + "/supersetnotequal": "\u228B", + "/supersetorequal": "\u2287", + "/superviseideographiccircled": "\u32AC", + "/superviseideographicparen": "\u323C", + "/surfer": "\u1F3C4", + "/sushi": "\u1F363", + "/suspensionRailway": "\u1F69F", + "/suspensiondbl": "\u2E44", + "/svfullwidth": "\u33DC", + "/svsquare": "\u33DC", + "/swatchtop": "\u23F1", + "/swimmer": "\u1F3CA", + "/swungdash": "\u2053", + "/symbolabovethreedotsabove": "\uFBB6", + "/symbolbelowthreedotsabove": "\uFBB7", + "/symboldotabove": "\uFBB2", + "/symboldotbelow": "\uFBB3", + "/symboldoubleverticalbarbelow": "\uFBBC", + "/symbolfourdotsabove": "\uFBBA", + "/symbolfourdotsbelow": "\uFBBB", + "/symbolpointingabovedownthreedotsabove": "\uFBB8", + "/symbolpointingbelowdownthreedotsabove": "\uFBB9", + "/symbolring": "\uFBBF", + "/symboltahabovesmall": "\uFBC0", + "/symboltahbelowsmall": "\uFBC1", + "/symboltwodotsabove": "\uFBB4", + "/symboltwodotsbelow": "\uFBB5", + "/symboltwodotsverticallyabove": "\uFBBD", + "/symboltwodotsverticallybelow": "\uFBBE", + "/symmetry": "\u232F", + "/synagogue": "\u1F54D", + "/syouwaerasquare": "\u337C", + "/syringe": "\u1F489", + "/t": "\u0074", + "/t-shirt": "\u1F455", + "/t.inferior": "\u209C", + "/tabengali": "\u09A4", + "/tableTennisPaddleAndBall": "\u1F3D3", + "/tacirclekatakana": "\u32DF", + "/tackcircleaboveup": "\u27DF", + "/tackdiaeresisupfunc": "\u2361", + "/tackdown": "\u22A4", + "/tackdownmod": "\u02D5", + "/tackjotdownfunc": "\u234E", + "/tackjotupfunc": "\u2355", + "/tackleft": "\u22A3", + "/tackleftright": "\u27DB", + "/tackoverbarupfunc": "\u2351", + "/tackright": "\u22A2", + "/tackunderlinedownfunc": "\u234A", + "/tackup": "\u22A5", + "/tackupmod": "\u02D4", + "/taco": "\u1F32E", + "/tadeva": "\u0924", + "/tagujarati": "\u0AA4", + "/tagurmukhi": "\u0A24", + "/tah": "\u0637", + "/tah.fina": "\uFEC2", + "/tah.init": "\uFEC3", + "/tah.init_alefmaksura.fina": "\uFCF5", + "/tah.init_hah.fina": "\uFC26", + "/tah.init_hah.medi": "\uFCB8", + "/tah.init_meem.fina": "\uFC27", + "/tah.init_meem.medi": "\uFD33", + "/tah.init_meem.medi_hah.medi": "\uFD72", + "/tah.init_meem.medi_meem.medi": "\uFD73", + "/tah.init_yeh.fina": "\uFCF6", + "/tah.isol": "\uFEC1", + "/tah.medi": "\uFEC4", + "/tah.medi_alefmaksura.fina": "\uFD11", + "/tah.medi_meem.medi": "\uFD3A", + "/tah.medi_meem.medi_hah.fina": "\uFD71", + "/tah.medi_meem.medi_yeh.fina": "\uFD74", + "/tah.medi_yeh.fina": "\uFD12", + "/tahabove": "\u0615", + "/taharabic": "\u0637", + "/tahfinalarabic": "\uFEC2", + "/tahinitialarabic": "\uFEC3", + "/tahiragana": "\u305F", + "/tahmedialarabic": "\uFEC4", + "/tahthreedotsabove": "\u069F", + "/taisyouerasquare": "\u337D", + "/takatakana": "\u30BF", + "/takatakanahalfwidth": "\uFF80", + "/takhallus": "\u0614", + "/talingvowel": "\uA9BA", + "/taml:a": "\u0B85", + "/taml:aa": "\u0B86", + "/taml:aasign": "\u0BBE", + "/taml:ai": "\u0B90", + "/taml:aisign": "\u0BC8", + "/taml:anusvarasign": "\u0B82", + "/taml:asabovesign": "\u0BF8", + "/taml:au": "\u0B94", + "/taml:aulengthmark": "\u0BD7", + "/taml:ausign": "\u0BCC", + "/taml:ca": "\u0B9A", + "/taml:creditsign": "\u0BF7", + "/taml:daysign": "\u0BF3", + "/taml:debitsign": "\u0BF6", + "/taml:e": "\u0B8E", + "/taml:ee": "\u0B8F", + "/taml:eesign": "\u0BC7", + "/taml:eight": "\u0BEE", + "/taml:esign": "\u0BC6", + "/taml:five": "\u0BEB", + "/taml:four": "\u0BEA", + "/taml:ha": "\u0BB9", + "/taml:i": "\u0B87", + "/taml:ii": "\u0B88", + "/taml:iisign": "\u0BC0", + "/taml:isign": "\u0BBF", + "/taml:ja": "\u0B9C", + "/taml:ka": "\u0B95", + "/taml:la": "\u0BB2", + "/taml:lla": "\u0BB3", + "/taml:llla": "\u0BB4", + "/taml:ma": "\u0BAE", + "/taml:monthsign": "\u0BF4", + "/taml:na": "\u0BA8", + "/taml:nga": "\u0B99", + "/taml:nine": "\u0BEF", + "/taml:nna": "\u0BA3", + "/taml:nnna": "\u0BA9", + "/taml:nya": "\u0B9E", + "/taml:o": "\u0B92", + "/taml:om": "\u0BD0", + "/taml:one": "\u0BE7", + "/taml:onehundred": "\u0BF1", + "/taml:onethousand": "\u0BF2", + "/taml:oo": "\u0B93", + "/taml:oosign": "\u0BCB", + "/taml:osign": "\u0BCA", + "/taml:pa": "\u0BAA", + "/taml:ra": "\u0BB0", + "/taml:rra": "\u0BB1", + "/taml:rupeesign": "\u0BF9", + "/taml:sa": "\u0BB8", + "/taml:seven": "\u0BED", + "/taml:sha": "\u0BB6", + "/taml:sign": "\u0BFA", + "/taml:six": "\u0BEC", + "/taml:ssa": "\u0BB7", + "/taml:ta": "\u0BA4", + "/taml:ten": "\u0BF0", + "/taml:three": "\u0BE9", + "/taml:tta": "\u0B9F", + "/taml:two": "\u0BE8", + "/taml:u": "\u0B89", + "/taml:usign": "\u0BC1", + "/taml:uu": "\u0B8A", + "/taml:uusign": "\u0BC2", + "/taml:va": "\u0BB5", + "/taml:viramasign": "\u0BCD", + "/taml:visargasign": "\u0B83", + "/taml:ya": "\u0BAF", + "/taml:yearsign": "\u0BF5", + "/taml:zero": "\u0BE6", + "/tamurda": "\uA9A1", + "/tanabataTree": "\u1F38B", + "/tangerine": "\u1F34A", + "/tapeCartridge": "\u1F5AD", + "/tarungvowel": "\uA9B4", + "/tatweelFathatanAbove": "\uFE71", + "/tatweelarabic": "\u0640", + "/tau": "\u03C4", + "/taurus": "\u2649", + "/tav": "\u05EA", + "/tav:hb": "\u05EA", + "/tavdages": "\uFB4A", + "/tavdagesh": "\uFB4A", + "/tavdageshhebrew": "\uFB4A", + "/tavhebrew": "\u05EA", + "/tavwide:hb": "\uFB28", + "/tavwithdagesh:hb": "\uFB4A", + "/taxi": "\u1F695", + "/tbar": "\u0167", + "/tbopomofo": "\u310A", + "/tcaron": "\u0165", + "/tccurl": "\u02A8", + "/tcedilla": "\u0163", + "/tcheh": "\u0686", + "/tcheh.fina": "\uFB7B", + "/tcheh.init": "\uFB7C", + "/tcheh.isol": "\uFB7A", + "/tcheh.medi": "\uFB7D", + "/tcheharabic": "\u0686", + "/tchehdotabove": "\u06BF", + "/tcheheh": "\u0687", + "/tcheheh.fina": "\uFB7F", + "/tcheheh.init": "\uFB80", + "/tcheheh.isol": "\uFB7E", + "/tcheheh.medi": "\uFB81", + "/tchehfinalarabic": "\uFB7B", + "/tchehinitialarabic": "\uFB7C", + "/tchehmedialarabic": "\uFB7D", + "/tchehmeeminitialarabic": "\uFB7C", + "/tcircle": "\u24E3", + "/tcircumflexbelow": "\u1E71", + "/tcommaaccent": "\u0163", + "/tcurl": "\u0236", + "/tdieresis": "\u1E97", + "/tdot": "\u1E6B", + "/tdotaccent": "\u1E6B", + "/tdotbelow": "\u1E6D", + "/teacupOutHandle": "\u1F375", + "/tear-offCalendar": "\u1F4C6", + "/tecirclekatakana": "\u32E2", + "/tecyr": "\u0442", + "/tecyrillic": "\u0442", + "/tedescendercyrillic": "\u04AD", + "/teh": "\u062A", + "/teh.fina": "\uFE96", + "/teh.init": "\uFE97", + "/teh.init_alefmaksura.fina": "\uFC0F", + "/teh.init_hah.fina": "\uFC0C", + "/teh.init_hah.medi": "\uFCA2", + "/teh.init_hah.medi_jeem.medi": "\uFD52", + "/teh.init_hah.medi_meem.medi": "\uFD53", + "/teh.init_heh.medi": "\uFCA5", + "/teh.init_jeem.fina": "\uFC0B", + "/teh.init_jeem.medi": "\uFCA1", + "/teh.init_jeem.medi_meem.medi": "\uFD50", + "/teh.init_khah.fina": "\uFC0D", + "/teh.init_khah.medi": "\uFCA3", + "/teh.init_khah.medi_meem.medi": "\uFD54", + "/teh.init_meem.fina": "\uFC0E", + "/teh.init_meem.medi": "\uFCA4", + "/teh.init_meem.medi_hah.medi": "\uFD56", + "/teh.init_meem.medi_jeem.medi": "\uFD55", + "/teh.init_meem.medi_khah.medi": "\uFD57", + "/teh.init_yeh.fina": "\uFC10", + "/teh.isol": "\uFE95", + "/teh.medi": "\uFE98", + "/teh.medi_alefmaksura.fina": "\uFC74", + "/teh.medi_hah.medi_jeem.fina": "\uFD51", + "/teh.medi_heh.medi": "\uFCE4", + "/teh.medi_jeem.medi_alefmaksura.fina": "\uFDA0", + "/teh.medi_jeem.medi_yeh.fina": "\uFD9F", + "/teh.medi_khah.medi_alefmaksura.fina": "\uFDA2", + "/teh.medi_khah.medi_yeh.fina": "\uFDA1", + "/teh.medi_meem.fina": "\uFC72", + "/teh.medi_meem.medi": "\uFCE3", + "/teh.medi_meem.medi_alefmaksura.fina": "\uFDA4", + "/teh.medi_meem.medi_yeh.fina": "\uFDA3", + "/teh.medi_noon.fina": "\uFC73", + "/teh.medi_reh.fina": "\uFC70", + "/teh.medi_yeh.fina": "\uFC75", + "/teh.medi_zain.fina": "\uFC71", + "/teharabic": "\u062A", + "/tehdownthreedotsabove": "\u067D", + "/teheh": "\u067F", + "/teheh.fina": "\uFB63", + "/teheh.init": "\uFB64", + "/teheh.isol": "\uFB62", + "/teheh.medi": "\uFB65", + "/tehfinalarabic": "\uFE96", + "/tehhahinitialarabic": "\uFCA2", + "/tehhahisolatedarabic": "\uFC0C", + "/tehinitialarabic": "\uFE97", + "/tehiragana": "\u3066", + "/tehjeeminitialarabic": "\uFCA1", + "/tehjeemisolatedarabic": "\uFC0B", + "/tehmarbuta": "\u0629", + "/tehmarbuta.fina": "\uFE94", + "/tehmarbuta.isol": "\uFE93", + "/tehmarbutaarabic": "\u0629", + "/tehmarbutafinalarabic": "\uFE94", + "/tehmarbutagoal": "\u06C3", + "/tehmedialarabic": "\uFE98", + "/tehmeeminitialarabic": "\uFCA4", + "/tehmeemisolatedarabic": "\uFC0E", + "/tehnoonfinalarabic": "\uFC73", + "/tehring": "\u067C", + "/tekatakana": "\u30C6", + "/tekatakanahalfwidth": "\uFF83", + "/telephone": "\u2121", + "/telephoneOnTopOfModem": "\u1F580", + "/telephoneReceiver": "\u1F4DE", + "/telephoneReceiverPage": "\u1F57C", + "/telephoneblack": "\u260E", + "/telephonerecorder": "\u2315", + "/telephonewhite": "\u260F", + "/telescope": "\u1F52D", + "/television": "\u1F4FA", + "/telishaGedolah:hb": "\u05A0", + "/telishaQetannah:hb": "\u05A9", + "/telishagedolahebrew": "\u05A0", + "/telishaqetanahebrew": "\u05A9", + "/telu:a": "\u0C05", + "/telu:aa": "\u0C06", + "/telu:aasign": "\u0C3E", + "/telu:ai": "\u0C10", + "/telu:ailengthmark": "\u0C56", + "/telu:aisign": "\u0C48", + "/telu:anusvarasign": "\u0C02", + "/telu:au": "\u0C14", + "/telu:ausign": "\u0C4C", + "/telu:avagrahasign": "\u0C3D", + "/telu:ba": "\u0C2C", + "/telu:bha": "\u0C2D", + "/telu:bindusigncandra": "\u0C01", + "/telu:ca": "\u0C1A", + "/telu:cha": "\u0C1B", + "/telu:combiningbinduabovesigncandra": "\u0C00", + "/telu:da": "\u0C26", + "/telu:dda": "\u0C21", + "/telu:ddha": "\u0C22", + "/telu:dha": "\u0C27", + "/telu:dza": "\u0C59", + "/telu:e": "\u0C0E", + "/telu:ee": "\u0C0F", + "/telu:eesign": "\u0C47", + "/telu:eight": "\u0C6E", + "/telu:esign": "\u0C46", + "/telu:five": "\u0C6B", + "/telu:four": "\u0C6A", + "/telu:fractiononeforevenpowersoffour": "\u0C7C", + "/telu:fractiononeforoddpowersoffour": "\u0C79", + "/telu:fractionthreeforevenpowersoffour": "\u0C7E", + "/telu:fractionthreeforoddpowersoffour": "\u0C7B", + "/telu:fractiontwoforevenpowersoffour": "\u0C7D", + "/telu:fractiontwoforoddpowersoffour": "\u0C7A", + "/telu:fractionzeroforoddpowersoffour": "\u0C78", + "/telu:ga": "\u0C17", + "/telu:gha": "\u0C18", + "/telu:ha": "\u0C39", + "/telu:i": "\u0C07", + "/telu:ii": "\u0C08", + "/telu:iisign": "\u0C40", + "/telu:isign": "\u0C3F", + "/telu:ja": "\u0C1C", + "/telu:jha": "\u0C1D", + "/telu:ka": "\u0C15", + "/telu:kha": "\u0C16", + "/telu:la": "\u0C32", + "/telu:lengthmark": "\u0C55", + "/telu:lla": "\u0C33", + "/telu:llla": "\u0C34", + "/telu:llsignvocal": "\u0C63", + "/telu:llvocal": "\u0C61", + "/telu:lsignvocal": "\u0C62", + "/telu:lvocal": "\u0C0C", + "/telu:ma": "\u0C2E", + "/telu:na": "\u0C28", + "/telu:nga": "\u0C19", + "/telu:nine": "\u0C6F", + "/telu:nna": "\u0C23", + "/telu:nya": "\u0C1E", + "/telu:o": "\u0C12", + "/telu:one": "\u0C67", + "/telu:oo": "\u0C13", + "/telu:oosign": "\u0C4B", + "/telu:osign": "\u0C4A", + "/telu:pa": "\u0C2A", + "/telu:pha": "\u0C2B", + "/telu:ra": "\u0C30", + "/telu:rra": "\u0C31", + "/telu:rrra": "\u0C5A", + "/telu:rrsignvocal": "\u0C44", + "/telu:rrvocal": "\u0C60", + "/telu:rsignvocal": "\u0C43", + "/telu:rvocal": "\u0C0B", + "/telu:sa": "\u0C38", + "/telu:seven": "\u0C6D", + "/telu:sha": "\u0C36", + "/telu:six": "\u0C6C", + "/telu:ssa": "\u0C37", + "/telu:ta": "\u0C24", + "/telu:tha": "\u0C25", + "/telu:three": "\u0C69", + "/telu:tsa": "\u0C58", + "/telu:tta": "\u0C1F", + "/telu:ttha": "\u0C20", + "/telu:tuumusign": "\u0C7F", + "/telu:two": "\u0C68", + "/telu:u": "\u0C09", + "/telu:usign": "\u0C41", + "/telu:uu": "\u0C0A", + "/telu:uusign": "\u0C42", + "/telu:va": "\u0C35", + "/telu:viramasign": "\u0C4D", + "/telu:visargasign": "\u0C03", + "/telu:ya": "\u0C2F", + "/telu:zero": "\u0C66", + "/ten.roman": "\u2169", + "/ten.romansmall": "\u2179", + "/tencircle": "\u2469", + "/tencircledbl": "\u24FE", + "/tencirclesquare": "\u3248", + "/tenge": "\u20B8", + "/tenhangzhou": "\u3038", + "/tenideographiccircled": "\u3289", + "/tenideographicparen": "\u3229", + "/tennisRacquetAndBall": "\u1F3BE", + "/tenparen": "\u247D", + "/tenparenthesized": "\u247D", + "/tenperiod": "\u2491", + "/tenroman": "\u2179", + "/tent": "\u26FA", + "/tenthousand.roman": "\u2182", + "/tesh": "\u02A7", + "/tet": "\u05D8", + "/tet:hb": "\u05D8", + "/tetailcyr": "\u04AD", + "/tetdagesh": "\uFB38", + "/tetdageshhebrew": "\uFB38", + "/tethebrew": "\u05D8", + "/tetrasememetrical": "\u23D8", + "/tetsecyr": "\u04B5", + "/tetsecyrillic": "\u04B5", + "/tetwithdagesh:hb": "\uFB38", + "/tevir:hb": "\u059B", + "/tevirhebrew": "\u059B", + "/tevirlefthebrew": "\u059B", + "/thabengali": "\u09A5", + "/thadeva": "\u0925", + "/thagujarati": "\u0AA5", + "/thagurmukhi": "\u0A25", + "/thai:angkhankhu": "\u0E5A", + "/thai:baht": "\u0E3F", + "/thai:bobaimai": "\u0E1A", + "/thai:chochan": "\u0E08", + "/thai:chochang": "\u0E0A", + "/thai:choching": "\u0E09", + "/thai:chochoe": "\u0E0C", + "/thai:dochada": "\u0E0E", + "/thai:dodek": "\u0E14", + "/thai:eight": "\u0E58", + "/thai:five": "\u0E55", + "/thai:fofa": "\u0E1D", + "/thai:fofan": "\u0E1F", + "/thai:fongman": "\u0E4F", + "/thai:four": "\u0E54", + "/thai:hohip": "\u0E2B", + "/thai:honokhuk": "\u0E2E", + "/thai:khokhai": "\u0E02", + "/thai:khokhon": "\u0E05", + "/thai:khokhuat": "\u0E03", + "/thai:khokhwai": "\u0E04", + "/thai:khomut": "\u0E5B", + "/thai:khorakhang": "\u0E06", + "/thai:kokai": "\u0E01", + "/thai:lakkhangyao": "\u0E45", + "/thai:lochula": "\u0E2C", + "/thai:loling": "\u0E25", + "/thai:lu": "\u0E26", + "/thai:maichattawa": "\u0E4B", + "/thai:maiek": "\u0E48", + "/thai:maihan-akat": "\u0E31", + "/thai:maitaikhu": "\u0E47", + "/thai:maitho": "\u0E49", + "/thai:maitri": "\u0E4A", + "/thai:maiyamok": "\u0E46", + "/thai:moma": "\u0E21", + "/thai:ngongu": "\u0E07", + "/thai:nikhahit": "\u0E4D", + "/thai:nine": "\u0E59", + "/thai:nonen": "\u0E13", + "/thai:nonu": "\u0E19", + "/thai:oang": "\u0E2D", + "/thai:one": "\u0E51", + "/thai:paiyannoi": "\u0E2F", + "/thai:phinthu": "\u0E3A", + "/thai:phophan": "\u0E1E", + "/thai:phophung": "\u0E1C", + "/thai:phosamphao": "\u0E20", + "/thai:popla": "\u0E1B", + "/thai:rorua": "\u0E23", + "/thai:ru": "\u0E24", + "/thai:saraa": "\u0E30", + "/thai:saraaa": "\u0E32", + "/thai:saraae": "\u0E41", + "/thai:saraaimaimalai": "\u0E44", + "/thai:saraaimaimuan": "\u0E43", + "/thai:saraam": "\u0E33", + "/thai:sarae": "\u0E40", + "/thai:sarai": "\u0E34", + "/thai:saraii": "\u0E35", + "/thai:sarao": "\u0E42", + "/thai:sarau": "\u0E38", + "/thai:saraue": "\u0E36", + "/thai:sarauee": "\u0E37", + "/thai:sarauu": "\u0E39", + "/thai:seven": "\u0E57", + "/thai:six": "\u0E56", + "/thai:sorusi": "\u0E29", + "/thai:sosala": "\u0E28", + "/thai:soso": "\u0E0B", + "/thai:sosua": "\u0E2A", + "/thai:thanthakhat": "\u0E4C", + "/thai:thonangmontho": "\u0E11", + "/thai:thophuthao": "\u0E12", + "/thai:thothahan": "\u0E17", + "/thai:thothan": "\u0E10", + "/thai:thothong": "\u0E18", + "/thai:thothung": "\u0E16", + "/thai:three": "\u0E53", + "/thai:topatak": "\u0E0F", + "/thai:totao": "\u0E15", + "/thai:two": "\u0E52", + "/thai:wowaen": "\u0E27", + "/thai:yamakkan": "\u0E4E", + "/thai:yoyak": "\u0E22", + "/thai:yoying": "\u0E0D", + "/thai:zero": "\u0E50", + "/thal": "\u0630", + "/thal.fina": "\uFEAC", + "/thal.init_superscriptalef.fina": "\uFC5B", + "/thal.isol": "\uFEAB", + "/thalarabic": "\u0630", + "/thalfinalarabic": "\uFEAC", + "/thanthakhatlowleftthai": "\uF898", + "/thanthakhatlowrightthai": "\uF897", + "/thanthakhatthai": "\u0E4C", + "/thanthakhatupperleftthai": "\uF896", + "/theh": "\u062B", + "/theh.fina": "\uFE9A", + "/theh.init": "\uFE9B", + "/theh.init_alefmaksura.fina": "\uFC13", + "/theh.init_jeem.fina": "\uFC11", + "/theh.init_meem.fina": "\uFC12", + "/theh.init_meem.medi": "\uFCA6", + "/theh.init_yeh.fina": "\uFC14", + "/theh.isol": "\uFE99", + "/theh.medi": "\uFE9C", + "/theh.medi_alefmaksura.fina": "\uFC7A", + "/theh.medi_heh.medi": "\uFCE6", + "/theh.medi_meem.fina": "\uFC78", + "/theh.medi_meem.medi": "\uFCE5", + "/theh.medi_noon.fina": "\uFC79", + "/theh.medi_reh.fina": "\uFC76", + "/theh.medi_yeh.fina": "\uFC7B", + "/theh.medi_zain.fina": "\uFC77", + "/theharabic": "\u062B", + "/thehfinalarabic": "\uFE9A", + "/thehinitialarabic": "\uFE9B", + "/thehmedialarabic": "\uFE9C", + "/thereexists": "\u2203", + "/therefore": "\u2234", + "/thermometer": "\u1F321", + "/theta": "\u03B8", + "/theta.math": "\u03D1", + "/theta1": "\u03D1", + "/thetasymbolgreek": "\u03D1", + "/thieuthacirclekorean": "\u3279", + "/thieuthaparenkorean": "\u3219", + "/thieuthcirclekorean": "\u326B", + "/thieuthkorean": "\u314C", + "/thieuthparenkorean": "\u320B", + "/thinspace": "\u2009", + "/thirteencircle": "\u246C", + "/thirteencircleblack": "\u24ED", + "/thirteenparen": "\u2480", + "/thirteenparenthesized": "\u2480", + "/thirteenperiod": "\u2494", + "/thirtycircle": "\u325A", + "/thirtycirclesquare": "\u324A", + "/thirtyeightcircle": "\u32B3", + "/thirtyfivecircle": "\u325F", + "/thirtyfourcircle": "\u325E", + "/thirtyhangzhou": "\u303A", + "/thirtyninecircle": "\u32B4", + "/thirtyonecircle": "\u325B", + "/thirtysevencircle": "\u32B2", + "/thirtysixcircle": "\u32B1", + "/thirtythreecircle": "\u325D", + "/thirtytwocircle": "\u325C", + "/thonangmonthothai": "\u0E11", + "/thook": "\u01AD", + "/thophuthaothai": "\u0E12", + "/thorn": "\u00FE", + "/thornstroke": "\uA765", + "/thornstrokedescender": "\uA767", + "/thothahanthai": "\u0E17", + "/thothanthai": "\u0E10", + "/thothongthai": "\u0E18", + "/thothungthai": "\u0E16", + "/thoughtBalloon": "\u1F4AD", + "/thousandcyrillic": "\u0482", + "/thousandscyr": "\u0482", + "/thousandsseparator": "\u066C", + "/thousandsseparatorarabic": "\u066C", + "/thousandsseparatorpersian": "\u066C", + "/three": "\u0033", + "/three.inferior": "\u2083", + "/three.roman": "\u2162", + "/three.romansmall": "\u2172", + "/threeButtonMouse": "\u1F5B1", + "/threeNetworkedComputers": "\u1F5A7", + "/threeRaysAbove": "\u1F5E4", + "/threeRaysBelow": "\u1F5E5", + "/threeRaysLeft": "\u1F5E6", + "/threeRaysRight": "\u1F5E7", + "/threeSpeechBubbles": "\u1F5EB", + "/threearabic": "\u0663", + "/threebengali": "\u09E9", + "/threecircle": "\u2462", + "/threecircledbl": "\u24F7", + "/threecircleinversesansserif": "\u278C", + "/threecomma": "\u1F104", + "/threedeva": "\u0969", + "/threedimensionalangle": "\u27C0", + "/threedotpunctuation": "\u2056", + "/threedotsaboveabove": "\u06DB", + "/threedsquare": "\u1F19B", + "/threeeighths": "\u215C", + "/threefar": "\u06F3", + "/threefifths": "\u2157", + "/threegujarati": "\u0AE9", + "/threegurmukhi": "\u0A69", + "/threehackarabic": "\u0663", + "/threehangzhou": "\u3023", + "/threeideographiccircled": "\u3282", + "/threeideographicparen": "\u3222", + "/threeinferior": "\u2083", + "/threelinesconvergingleft": "\u269F", + "/threelinesconvergingright": "\u269E", + "/threemonospace": "\uFF13", + "/threenumeratorbengali": "\u09F6", + "/threeoldstyle": "\uF733", + "/threeparen": "\u2476", + "/threeparenthesized": "\u2476", + "/threeperemspace": "\u2004", + "/threeperiod": "\u248A", + "/threepersian": "\u06F3", + "/threequarters": "\u00BE", + "/threequartersemdash": "\uF6DE", + "/threerightarrows": "\u21F6", + "/threeroman": "\u2172", + "/threesuperior": "\u00B3", + "/threethai": "\u0E53", + "/thumbsDownSign": "\u1F44E", + "/thumbsUpSign": "\u1F44D", + "/thundercloudrain": "\u26C8", + "/thunderstorm": "\u2608", + "/thzfullwidth": "\u3394", + "/thzsquare": "\u3394", + "/tibt:AA": "\u0F60", + "/tibt:a": "\u0F68", + "/tibt:aavowelsign": "\u0F71", + "/tibt:angkhanggyasmark": "\u0F3D", + "/tibt:angkhanggyonmark": "\u0F3C", + "/tibt:astrologicalkhyudpasign": "\u0F18", + "/tibt:astrologicalsdongtshugssign": "\u0F19", + "/tibt:astrologicalsgragcancharrtagssign": "\u0F17", + "/tibt:asubjoined": "\u0FB8", + "/tibt:ba": "\u0F56", + "/tibt:basubjoined": "\u0FA6", + "/tibt:bha": "\u0F57", + "/tibt:bhasubjoined": "\u0FA7", + "/tibt:bkashogyigmgomark": "\u0F0A", + "/tibt:brdarnyingyigmgomdunmainitialmark": "\u0FD3", + "/tibt:brdarnyingyigmgosgabmaclosingmark": "\u0FD4", + "/tibt:bsdusrtagsmark": "\u0F34", + "/tibt:bskashoggimgorgyanmark": "\u0FD0", + "/tibt:bskuryigmgomark": "\u0F09", + "/tibt:ca": "\u0F45", + "/tibt:cangteucantillationsign": "\u0FC2", + "/tibt:caretdzudrtagsbzhimigcanmark": "\u0F36", + "/tibt:caretdzudrtagsmelongcanmark": "\u0F13", + "/tibt:caretyigmgophurshadmamark": "\u0F06", + "/tibt:casubjoined": "\u0F95", + "/tibt:cha": "\u0F46", + "/tibt:chadrtagslogotypesign": "\u0F15", + "/tibt:chasubjoined": "\u0F96", + "/tibt:chemgomark": "\u0F38", + "/tibt:da": "\u0F51", + "/tibt:dasubjoined": "\u0FA1", + "/tibt:dda": "\u0F4C", + "/tibt:ddasubjoined": "\u0F9C", + "/tibt:ddha": "\u0F4D", + "/tibt:ddhasubjoined": "\u0F9D", + "/tibt:delimitertshegbstarmark": "\u0F0C", + "/tibt:dha": "\u0F52", + "/tibt:dhasubjoined": "\u0FA2", + "/tibt:drilbusymbol": "\u0FC4", + "/tibt:dza": "\u0F5B", + "/tibt:dzasubjoined": "\u0FAB", + "/tibt:dzha": "\u0F5C", + "/tibt:dzhasubjoined": "\u0FAC", + "/tibt:eevowelsign": "\u0F7B", + "/tibt:eight": "\u0F28", + "/tibt:evowelsign": "\u0F7A", + "/tibt:five": "\u0F25", + "/tibt:four": "\u0F24", + "/tibt:ga": "\u0F42", + "/tibt:gasubjoined": "\u0F92", + "/tibt:gha": "\u0F43", + "/tibt:ghasubjoined": "\u0F93", + "/tibt:grucanrgyingssign": "\u0F8A", + "/tibt:grumedrgyingssign": "\u0F8B", + "/tibt:gtertshegmark": "\u0F14", + "/tibt:gteryigmgotruncatedamark": "\u0F01", + "/tibt:gteryigmgoumgtertshegmamark": "\u0F03", + "/tibt:gteryigmgoumrnambcadmamark": "\u0F02", + "/tibt:gugrtagsgyasmark": "\u0F3B", + "/tibt:gugrtagsgyonmark": "\u0F3A", + "/tibt:ha": "\u0F67", + "/tibt:halantamark": "\u0F84", + "/tibt:halfeight": "\u0F31", + "/tibt:halffive": "\u0F2E", + "/tibt:halffour": "\u0F2D", + "/tibt:halfnine": "\u0F32", + "/tibt:halfone": "\u0F2A", + "/tibt:halfseven": "\u0F30", + "/tibt:halfsix": "\u0F2F", + "/tibt:halfthree": "\u0F2C", + "/tibt:halftwo": "\u0F2B", + "/tibt:halfzero": "\u0F33", + "/tibt:hasubjoined": "\u0FB7", + "/tibt:heavybeatcantillationsign": "\u0FC0", + "/tibt:iivowelsign": "\u0F73", + "/tibt:intersyllabictshegmark": "\u0F0B", + "/tibt:invertedmchucansign": "\u0F8C", + "/tibt:invertedmchucansubjoinedsign": "\u0F8F", + "/tibt:ivowelsign": "\u0F72", + "/tibt:ja": "\u0F47", + "/tibt:jasubjoined": "\u0F97", + "/tibt:ka": "\u0F40", + "/tibt:kasubjoined": "\u0F90", + "/tibt:kha": "\u0F41", + "/tibt:khasubjoined": "\u0F91", + "/tibt:kka": "\u0F6B", + "/tibt:kssa": "\u0F69", + "/tibt:kssasubjoined": "\u0FB9", + "/tibt:kurukha": "\u0FBE", + "/tibt:kurukhabzhimigcan": "\u0FBF", + "/tibt:la": "\u0F63", + "/tibt:lasubjoined": "\u0FB3", + "/tibt:lcetsacansign": "\u0F88", + "/tibt:lcetsacansubjoinedsign": "\u0F8D", + "/tibt:lcirtagssign": "\u0F86", + "/tibt:leadingmchanrtagsmark": "\u0FD9", + "/tibt:lhagrtagslogotypesign": "\u0F16", + "/tibt:lightbeatcantillationsign": "\u0FC1", + "/tibt:llvocalicvowelsign": "\u0F79", + "/tibt:lvocalicvowelsign": "\u0F78", + "/tibt:ma": "\u0F58", + "/tibt:martshessign": "\u0F3F", + "/tibt:masubjoined": "\u0FA8", + "/tibt:mchucansign": "\u0F89", + "/tibt:mchucansubjoinedsign": "\u0F8E", + "/tibt:mnyamyiggimgorgyanmark": "\u0FD1", + "/tibt:na": "\u0F53", + "/tibt:nasubjoined": "\u0FA3", + "/tibt:nga": "\u0F44", + "/tibt:ngasbzungnyizlamark": "\u0F35", + "/tibt:ngasbzungsgorrtagsmark": "\u0F37", + "/tibt:ngasubjoined": "\u0F94", + "/tibt:nine": "\u0F29", + "/tibt:nna": "\u0F4E", + "/tibt:nnasubjoined": "\u0F9E", + "/tibt:norbubzhikhyilsymbol": "\u0FCC", + "/tibt:norbugsumkhyilsymbol": "\u0FCB", + "/tibt:norbunyiskhyilsymbol": "\u0FCA", + "/tibt:norbusymbol": "\u0FC9", + "/tibt:nya": "\u0F49", + "/tibt:nyasubjoined": "\u0F99", + "/tibt:nyisshadmark": "\u0F0E", + "/tibt:nyistshegmark": "\u0FD2", + "/tibt:nyistshegshadmark": "\u0F10", + "/tibt:nyizlanaadasign": "\u0F82", + "/tibt:omsyllable": "\u0F00", + "/tibt:one": "\u0F21", + "/tibt:oovowelsign": "\u0F7D", + "/tibt:ovowelsign": "\u0F7C", + "/tibt:pa": "\u0F54", + "/tibt:padmagdansymbol": "\u0FC6", + "/tibt:palutamark": "\u0F85", + "/tibt:pasubjoined": "\u0FA4", + "/tibt:pha": "\u0F55", + "/tibt:phasubjoined": "\u0FA5", + "/tibt:phurpasymbol": "\u0FC8", + "/tibt:ra": "\u0F62", + "/tibt:rafixed": "\u0F6A", + "/tibt:rasubjoined": "\u0FB2", + "/tibt:rasubjoinedfixed": "\u0FBC", + "/tibt:rdeldkargcigsign": "\u0F1A", + "/tibt:rdeldkargnyissign": "\u0F1B", + "/tibt:rdeldkargsumsign": "\u0F1C", + "/tibt:rdeldkarrdelnagsign": "\u0F1F", + "/tibt:rdelnaggcigsign": "\u0F1D", + "/tibt:rdelnaggnyissign": "\u0F1E", + "/tibt:rdelnaggsumsign": "\u0FCF", + "/tibt:rdelnagrdeldkarsign": "\u0FCE", + "/tibt:rdorjergyagramsymbol": "\u0FC7", + "/tibt:rdorjesymbol": "\u0FC5", + "/tibt:reversediivowelsign": "\u0F81", + "/tibt:reversedivowelsign": "\u0F80", + "/tibt:rgyagramshadmark": "\u0F12", + "/tibt:rinchenspungsshadmark": "\u0F11", + "/tibt:rjessungarosign": "\u0F7E", + "/tibt:rnambcadsign": "\u0F7F", + "/tibt:rra": "\u0F6C", + "/tibt:rrvocalicvowelsign": "\u0F77", + "/tibt:rvocalicvowelsign": "\u0F76", + "/tibt:sa": "\u0F66", + "/tibt:sasubjoined": "\u0FB6", + "/tibt:sbrulshadmark": "\u0F08", + "/tibt:sbubchalcantillationsign": "\u0FC3", + "/tibt:seven": "\u0F27", + "/tibt:sha": "\u0F64", + "/tibt:shadmark": "\u0F0D", + "/tibt:shasubjoined": "\u0FB4", + "/tibt:six": "\u0F26", + "/tibt:snaldansign": "\u0F83", + "/tibt:ssa": "\u0F65", + "/tibt:ssasubjoined": "\u0FB5", + "/tibt:subjoinedAA": "\u0FB0", + "/tibt:svastileft": "\u0FD6", + "/tibt:svastileftdot": "\u0FD8", + "/tibt:svastiright": "\u0FD5", + "/tibt:svastirightdot": "\u0FD7", + "/tibt:ta": "\u0F4F", + "/tibt:tasubjoined": "\u0F9F", + "/tibt:tha": "\u0F50", + "/tibt:thasubjoined": "\u0FA0", + "/tibt:three": "\u0F23", + "/tibt:trailingmchanrtagsmark": "\u0FDA", + "/tibt:tsa": "\u0F59", + "/tibt:tsaphrumark": "\u0F39", + "/tibt:tsasubjoined": "\u0FA9", + "/tibt:tsha": "\u0F5A", + "/tibt:tshasubjoined": "\u0FAA", + "/tibt:tshegshadmark": "\u0F0F", + "/tibt:tta": "\u0F4A", + "/tibt:ttasubjoined": "\u0F9A", + "/tibt:ttha": "\u0F4B", + "/tibt:tthasubjoined": "\u0F9B", + "/tibt:two": "\u0F22", + "/tibt:uuvowelsign": "\u0F75", + "/tibt:uvowelsign": "\u0F74", + "/tibt:wa": "\u0F5D", + "/tibt:wasubjoined": "\u0FAD", + "/tibt:wasubjoinedfixed": "\u0FBA", + "/tibt:ya": "\u0F61", + "/tibt:yangrtagssign": "\u0F87", + "/tibt:yartshessign": "\u0F3E", + "/tibt:yasubjoined": "\u0FB1", + "/tibt:yasubjoinedfixed": "\u0FBB", + "/tibt:yigmgomdunmainitialmark": "\u0F04", + "/tibt:yigmgosgabmaclosingmark": "\u0F05", + "/tibt:yigmgotshegshadmamark": "\u0F07", + "/tibt:za": "\u0F5F", + "/tibt:zasubjoined": "\u0FAF", + "/tibt:zero": "\u0F20", + "/tibt:zha": "\u0F5E", + "/tibt:zhasubjoined": "\u0FAE", + "/ticirclekatakana": "\u32E0", + "/tickconvavediamondleftwhite": "\u27E2", + "/tickconvavediamondrightwhite": "\u27E3", + "/ticket": "\u1F3AB", + "/tickleftwhitesquare": "\u27E4", + "/tickrightwhitesquare": "\u27E5", + "/tifcha:hb": "\u0596", + "/tiger": "\u1F405", + "/tigerFace": "\u1F42F", + "/tihiragana": "\u3061", + "/tikatakana": "\u30C1", + "/tikatakanahalfwidth": "\uFF81", + "/tikeutacirclekorean": "\u3270", + "/tikeutaparenkorean": "\u3210", + "/tikeutcirclekorean": "\u3262", + "/tikeutkorean": "\u3137", + "/tikeutparenkorean": "\u3202", + "/tilde": "\u02DC", + "/tildebelowcmb": "\u0330", + "/tildecmb": "\u0303", + "/tildecomb": "\u0303", + "/tildediaeresisfunc": "\u2368", + "/tildedotaccent": "\u2E1E", + "/tildedotbelow": "\u2E1F", + "/tildedoublecmb": "\u0360", + "/tildeequalsreversed": "\u22CD", + "/tildelowmod": "\u02F7", + "/tildeoperator": "\u223C", + "/tildeoverlaycmb": "\u0334", + "/tildereversed": "\u223D", + "/tildering": "\u2E1B", + "/tildetpl": "\u224B", + "/tildeverticalcmb": "\u033E", + "/timerclock": "\u23F2", + "/timescircle": "\u2297", + "/tinsular": "\uA787", + "/tipehahebrew": "\u0596", + "/tipehalefthebrew": "\u0596", + "/tippigurmukhi": "\u0A70", + "/tiredFace": "\u1F62B", + "/tironiansignet": "\u204A", + "/tirtatumetespada": "\uA9DE", + "/titlocmbcyr": "\u0483", + "/titlocyrilliccmb": "\u0483", + "/tiwnarmenian": "\u057F", + "/tjekomicyr": "\u050F", + "/tlinebelow": "\u1E6F", + "/tmonospace": "\uFF54", + "/toarmenian": "\u0569", + "/tocirclekatakana": "\u32E3", + "/tocornerarrowNW": "\u21F1", + "/tocornerarrowSE": "\u21F2", + "/tohiragana": "\u3068", + "/toilet": "\u1F6BD", + "/tokatakana": "\u30C8", + "/tokatakanahalfwidth": "\uFF84", + "/tokyoTower": "\u1F5FC", + "/tolongvowel": "\uA9B5", + "/tomato": "\u1F345", + "/tonebarextrahighmod": "\u02E5", + "/tonebarextralowmod": "\u02E9", + "/tonebarhighmod": "\u02E6", + "/tonebarlowmod": "\u02E8", + "/tonebarmidmod": "\u02E7", + "/tonefive": "\u01BD", + "/tonehighbeginmod": "\u02F9", + "/tonehighendmod": "\u02FA", + "/tonelowbeginmod": "\u02FB", + "/tonelowendmod": "\u02FC", + "/tonesix": "\u0185", + "/tonetwo": "\u01A8", + "/tongue": "\u1F445", + "/tonos": "\u0384", + "/tonsquare": "\u3327", + "/topHat": "\u1F3A9", + "/topUpwardsArrowAbove": "\u1F51D", + "/topatakthai": "\u0E0F", + "/tortoiseshellbracketleft": "\u3014", + "/tortoiseshellbracketleftsmall": "\uFE5D", + "/tortoiseshellbracketleftvertical": "\uFE39", + "/tortoiseshellbracketright": "\u3015", + "/tortoiseshellbracketrightsmall": "\uFE5E", + "/tortoiseshellbracketrightvertical": "\uFE3A", + "/totalrunout": "\u2330", + "/totaothai": "\u0E15", + "/tpalatalhook": "\u01AB", + "/tparen": "\u24AF", + "/tparenthesized": "\u24AF", + "/trackball": "\u1F5B2", + "/tractor": "\u1F69C", + "/trademark": "\u2122", + "/trademarksans": "\uF8EA", + "/trademarkserif": "\uF6DB", + "/train": "\u1F686", + "/tram": "\u1F68A", + "/tramCar": "\u1F68B", + "/trapeziumwhite": "\u23E2", + "/tresillo": "\uA72B", + "/tretroflex": "\u0288", + "/tretroflexhook": "\u0288", + "/triagdn": "\u25BC", + "/triaglf": "\u25C4", + "/triagrt": "\u25BA", + "/triagup": "\u25B2", + "/triangleWithRoundedCorners": "\u1F6C6", + "/triangledotupwhite": "\u25EC", + "/triangledownblack": "\u25BC", + "/triangledownsmallblack": "\u25BE", + "/triangledownsmallwhite": "\u25BF", + "/triangledownwhite": "\u25BD", + "/trianglehalfupleftblack": "\u25ED", + "/trianglehalfuprightblack": "\u25EE", + "/triangleleftblack": "\u25C0", + "/triangleleftsmallblack": "\u25C2", + "/triangleleftsmallwhite": "\u25C3", + "/triangleleftwhite": "\u25C1", + "/triangleright": "\u22BF", + "/trianglerightblack": "\u25B6", + "/trianglerightsmallblack": "\u25B8", + "/trianglerightsmallwhite": "\u25B9", + "/trianglerightwhite": "\u25B7", + "/triangleupblack": "\u25B2", + "/triangleupsmallblack": "\u25B4", + "/triangleupsmallwhite": "\u25B5", + "/triangleupwhite": "\u25B3", + "/triangularFlagOnPost": "\u1F6A9", + "/triangularRuler": "\u1F4D0", + "/triangularbullet": "\u2023", + "/tricolon": "\u205D", + "/tricontainingtriwhiteanglesmall": "\u27C1", + "/tridentEmblem": "\u1F531", + "/trigramearth": "\u2637", + "/trigramfire": "\u2632", + "/trigramheaven": "\u2630", + "/trigramlake": "\u2631", + "/trigrammountain": "\u2636", + "/trigramthunder": "\u2633", + "/trigramwater": "\u2635", + "/trigramwind": "\u2634", + "/triplearrowleft": "\u21DA", + "/triplearrowright": "\u21DB", + "/tripledot": "\u061E", + "/trisememetrical": "\u23D7", + "/trns:baby": "\u1F6BC", + "/trolleybus": "\u1F68E", + "/trophy": "\u1F3C6", + "/tropicalDrink": "\u1F379", + "/tropicalFish": "\u1F420", + "/truckblack": "\u26DF", + "/true": "\u22A8", + "/trumpet": "\u1F3BA", + "/ts": "\u02A6", + "/tsadi": "\u05E6", + "/tsadi:hb": "\u05E6", + "/tsadidagesh": "\uFB46", + "/tsadidageshhebrew": "\uFB46", + "/tsadihebrew": "\u05E6", + "/tsadiwithdagesh:hb": "\uFB46", + "/tsecyr": "\u0446", + "/tsecyrillic": "\u0446", + "/tsere": "\u05B5", + "/tsere12": "\u05B5", + "/tsere1e": "\u05B5", + "/tsere2b": "\u05B5", + "/tsere:hb": "\u05B5", + "/tserehebrew": "\u05B5", + "/tserenarrowhebrew": "\u05B5", + "/tserequarterhebrew": "\u05B5", + "/tserewidehebrew": "\u05B5", + "/tshecyr": "\u045B", + "/tshecyrillic": "\u045B", + "/tsinnorit:hb": "\u05AE", + "/tstroke": "\u2C66", + "/tsuperior": "\uF6F3", + "/ttabengali": "\u099F", + "/ttadeva": "\u091F", + "/ttagujarati": "\u0A9F", + "/ttagurmukhi": "\u0A1F", + "/ttamahaprana": "\uA99C", + "/tteh": "\u0679", + "/tteh.fina": "\uFB67", + "/tteh.init": "\uFB68", + "/tteh.isol": "\uFB66", + "/tteh.medi": "\uFB69", + "/tteharabic": "\u0679", + "/tteheh": "\u067A", + "/tteheh.fina": "\uFB5F", + "/tteheh.init": "\uFB60", + "/tteheh.isol": "\uFB5E", + "/tteheh.medi": "\uFB61", + "/ttehfinalarabic": "\uFB67", + "/ttehinitialarabic": "\uFB68", + "/ttehmedialarabic": "\uFB69", + "/tthabengali": "\u09A0", + "/tthadeva": "\u0920", + "/tthagujarati": "\u0AA0", + "/tthagurmukhi": "\u0A20", + "/tturned": "\u0287", + "/tucirclekatakana": "\u32E1", + "/tugrik": "\u20AE", + "/tuhiragana": "\u3064", + "/tukatakana": "\u30C4", + "/tukatakanahalfwidth": "\uFF82", + "/tulip": "\u1F337", + "/tum": "\uA777", + "/turkishlira": "\u20BA", + "/turnedOkHandSign": "\u1F58F", + "/turnedcomma": "\u2E32", + "/turneddagger": "\u2E38", + "/turneddigitthree": "\u218B", + "/turneddigittwo": "\u218A", + "/turnedpiselehpada": "\uA9CD", + "/turnedsemicolon": "\u2E35", + "/turnedshogipieceblack": "\u26CA", + "/turnedshogipiecewhite": "\u26C9", + "/turnstiledblverticalbarright": "\u22AB", + "/turnstileleftrightdbl": "\u27DA", + "/turnstiletplverticalbarright": "\u22AA", + "/turtle": "\u1F422", + "/tusmallhiragana": "\u3063", + "/tusmallkatakana": "\u30C3", + "/tusmallkatakanahalfwidth": "\uFF6F", + "/twelve.roman": "\u216B", + "/twelve.romansmall": "\u217B", + "/twelvecircle": "\u246B", + "/twelvecircleblack": "\u24EC", + "/twelveparen": "\u247F", + "/twelveparenthesized": "\u247F", + "/twelveperiod": "\u2493", + "/twelveroman": "\u217B", + "/twenty-twopointtwosquare": "\u1F1A2", + "/twentycircle": "\u2473", + "/twentycircleblack": "\u24F4", + "/twentycirclesquare": "\u3249", + "/twentyeightcircle": "\u3258", + "/twentyfivecircle": "\u3255", + "/twentyfourcircle": "\u3254", + "/twentyhangzhou": "\u5344", + "/twentyninecircle": "\u3259", + "/twentyonecircle": "\u3251", + "/twentyparen": "\u2487", + "/twentyparenthesized": "\u2487", + "/twentyperiod": "\u249B", + "/twentysevencircle": "\u3257", + "/twentysixcircle": "\u3256", + "/twentythreecircle": "\u3253", + "/twentytwocircle": "\u3252", + "/twistedRightwardsArrows": "\u1F500", + "/two": "\u0032", + "/two.inferior": "\u2082", + "/two.roman": "\u2161", + "/two.romansmall": "\u2171", + "/twoButtonMouse": "\u1F5B0", + "/twoHearts": "\u1F495", + "/twoMenHoldingHands": "\u1F46C", + "/twoSpeechBubbles": "\u1F5EA", + "/twoWomenHoldingHands": "\u1F46D", + "/twoarabic": "\u0662", + "/twoasterisksalignedvertically": "\u2051", + "/twobengali": "\u09E8", + "/twocircle": "\u2461", + "/twocircledbl": "\u24F6", + "/twocircleinversesansserif": "\u278B", + "/twocomma": "\u1F103", + "/twodeva": "\u0968", + "/twodotenleader": "\u2025", + "/twodotleader": "\u2025", + "/twodotleadervertical": "\uFE30", + "/twodotpunctuation": "\u205A", + "/twodotsoveronedot": "\u2E2A", + "/twofar": "\u06F2", + "/twofifths": "\u2156", + "/twogujarati": "\u0AE8", + "/twogurmukhi": "\u0A68", + "/twohackarabic": "\u0662", + "/twohangzhou": "\u3022", + "/twoideographiccircled": "\u3281", + "/twoideographicparen": "\u3221", + "/twoinferior": "\u2082", + "/twoksquare": "\u1F19D", + "/twomonospace": "\uFF12", + "/twonumeratorbengali": "\u09F5", + "/twooldstyle": "\uF732", + "/twoparen": "\u2475", + "/twoparenthesized": "\u2475", + "/twoperiod": "\u2489", + "/twopersian": "\u06F2", + "/tworoman": "\u2171", + "/twoshortsjoinedmetrical": "\u23D6", + "/twoshortsoverlongmetrical": "\u23D5", + "/twostroke": "\u01BB", + "/twosuperior": "\u00B2", + "/twothai": "\u0E52", + "/twothirds": "\u2154", + "/twowayleftwaytrafficblack": "\u26D6", + "/twowayleftwaytrafficwhite": "\u26D7", + "/tz": "\uA729", + "/u": "\u0075", + "/u.fina": "\uFBD8", + "/u.isol": "\uFBD7", + "/uacute": "\u00FA", + "/uacutedblcyr": "\u04F3", + "/ubar": "\u0289", + "/ubengali": "\u0989", + "/ubopomofo": "\u3128", + "/ubracketleft": "\u2E26", + "/ubracketright": "\u2E27", + "/ubreve": "\u016D", + "/ucaron": "\u01D4", + "/ucircle": "\u24E4", + "/ucirclekatakana": "\u32D2", + "/ucircumflex": "\u00FB", + "/ucircumflexbelow": "\u1E77", + "/ucyr": "\u0443", + "/ucyrillic": "\u0443", + "/udattadeva": "\u0951", + "/udblacute": "\u0171", + "/udblgrave": "\u0215", + "/udeva": "\u0909", + "/udieresis": "\u00FC", + "/udieresisacute": "\u01D8", + "/udieresisbelow": "\u1E73", + "/udieresiscaron": "\u01DA", + "/udieresiscyr": "\u04F1", + "/udieresiscyrillic": "\u04F1", + "/udieresisgrave": "\u01DC", + "/udieresismacron": "\u01D6", + "/udotbelow": "\u1EE5", + "/ugrave": "\u00F9", + "/ugravedbl": "\u0215", + "/ugujarati": "\u0A89", + "/ugurmukhi": "\u0A09", + "/uhamza": "\u0677", + "/uhamza.isol": "\uFBDD", + "/uhdsquare": "\u1F1AB", + "/uhiragana": "\u3046", + "/uhoi": "\u1EE7", + "/uhookabove": "\u1EE7", + "/uhorn": "\u01B0", + "/uhornacute": "\u1EE9", + "/uhorndotbelow": "\u1EF1", + "/uhorngrave": "\u1EEB", + "/uhornhoi": "\u1EED", + "/uhornhookabove": "\u1EED", + "/uhorntilde": "\u1EEF", + "/uhungarumlaut": "\u0171", + "/uhungarumlautcyrillic": "\u04F3", + "/uighurkazakhkirghizalefmaksura.init": "\uFBE8", + "/uighurkazakhkirghizalefmaksura.medi": "\uFBE9", + "/uighurkirghizyeh.init_hamzaabove.medi_alefmaksura.fina": "\uFBF9", + "/uighurkirghizyeh.init_hamzaabove.medi_alefmaksura.medi": "\uFBFB", + "/uighurkirghizyeh.medi_hamzaabove.medi_alefmaksura.fina": "\uFBFA", + "/uinvertedbreve": "\u0217", + "/ukatakana": "\u30A6", + "/ukatakanahalfwidth": "\uFF73", + "/ukcyr": "\u0479", + "/ukcyrillic": "\u0479", + "/ukorean": "\u315C", + "/um": "\uA778", + "/umacron": "\u016B", + "/umacroncyr": "\u04EF", + "/umacroncyrillic": "\u04EF", + "/umacrondieresis": "\u1E7B", + "/umatragurmukhi": "\u0A41", + "/umbrella": "\u2602", + "/umbrellaonground": "\u26F1", + "/umbrellaraindrops": "\u2614", + "/umonospace": "\uFF55", + "/unamusedFace": "\u1F612", + "/unaspiratedmod": "\u02ED", + "/underscore": "\u005F", + "/underscorecenterline": "\uFE4E", + "/underscoredashed": "\uFE4D", + "/underscoredbl": "\u2017", + "/underscoremonospace": "\uFF3F", + "/underscorevertical": "\uFE33", + "/underscorewavy": "\uFE4F", + "/underscorewavyvertical": "\uFE34", + "/undertie": "\u203F", + "/undo": "\u238C", + "/union": "\u222A", + "/unionarray": "\u22C3", + "/uniondbl": "\u22D3", + "/universal": "\u2200", + "/unmarriedpartnership": "\u26AF", + "/uogonek": "\u0173", + "/uonsquare": "\u3306", + "/upPointingAirplane": "\u1F6E7", + "/upPointingMilitaryAirplane": "\u1F6E6", + "/upPointingSmallAirplane": "\u1F6E8", + "/uparen": "\u24B0", + "/uparenthesized": "\u24B0", + "/uparrowleftofdownarrow": "\u21C5", + "/upblock": "\u2580", + "/updblhorzsng": "\u2568", + "/updblleftsng": "\u255C", + "/updblrightsng": "\u2559", + "/upheavydnhorzlight": "\u2540", + "/upheavyhorzlight": "\u2538", + "/upheavyleftdnlight": "\u2526", + "/upheavyleftlight": "\u251A", + "/upheavyrightdnlight": "\u251E", + "/upheavyrightlight": "\u2516", + "/uplightdnhorzheavy": "\u2548", + "/uplighthorzheavy": "\u2537", + "/uplightleftdnheavy": "\u252A", + "/uplightleftheavy": "\u2519", + "/uplightrightdnheavy": "\u2522", + "/uplightrightheavy": "\u2515", + "/upperHalfBlock": "\u2580", + "/upperOneEighthBlock": "\u2594", + "/upperRightShadowedWhiteCircle": "\u1F53F", + "/upperdothebrew": "\u05C4", + "/upperhalfcircle": "\u25E0", + "/upperhalfcircleinversewhite": "\u25DA", + "/upperquadrantcirculararcleft": "\u25DC", + "/upperquadrantcirculararcright": "\u25DD", + "/uppertriangleleft": "\u25F8", + "/uppertriangleleftblack": "\u25E4", + "/uppertriangleright": "\u25F9", + "/uppertrianglerightblack": "\u25E5", + "/upsideDownFace": "\u1F643", + "/upsilon": "\u03C5", + "/upsilonacute": "\u1F7B", + "/upsilonasper": "\u1F51", + "/upsilonasperacute": "\u1F55", + "/upsilonaspergrave": "\u1F53", + "/upsilonaspertilde": "\u1F57", + "/upsilonbreve": "\u1FE0", + "/upsilondieresis": "\u03CB", + "/upsilondieresisacute": "\u1FE3", + "/upsilondieresisgrave": "\u1FE2", + "/upsilondieresistilde": "\u1FE7", + "/upsilondieresistonos": "\u03B0", + "/upsilongrave": "\u1F7A", + "/upsilonlatin": "\u028A", + "/upsilonlenis": "\u1F50", + "/upsilonlenisacute": "\u1F54", + "/upsilonlenisgrave": "\u1F52", + "/upsilonlenistilde": "\u1F56", + "/upsilontilde": "\u1FE6", + "/upsilontonos": "\u03CD", + "/upsilonwithmacron": "\u1FE1", + "/upsnghorzdbl": "\u2567", + "/upsngleftdbl": "\u255B", + "/upsngrightdbl": "\u2558", + "/uptackbelowcmb": "\u031D", + "/uptackmod": "\u02D4", + "/upwithexclamationmarksquare": "\u1F199", + "/uragurmukhi": "\u0A73", + "/uranus": "\u2645", + "/uring": "\u016F", + "/ushortcyr": "\u045E", + "/ushortcyrillic": "\u045E", + "/usmallhiragana": "\u3045", + "/usmallkatakana": "\u30A5", + "/usmallkatakanahalfwidth": "\uFF69", + "/usmod": "\uA770", + "/ustraightcyr": "\u04AF", + "/ustraightcyrillic": "\u04AF", + "/ustraightstrokecyr": "\u04B1", + "/ustraightstrokecyrillic": "\u04B1", + "/utilde": "\u0169", + "/utildeacute": "\u1E79", + "/utildebelow": "\u1E75", + "/uubengali": "\u098A", + "/uudeva": "\u090A", + "/uugujarati": "\u0A8A", + "/uugurmukhi": "\u0A0A", + "/uumatragurmukhi": "\u0A42", + "/uuvowelsignbengali": "\u09C2", + "/uuvowelsigndeva": "\u0942", + "/uuvowelsigngujarati": "\u0AC2", + "/uvowelsignbengali": "\u09C1", + "/uvowelsigndeva": "\u0941", + "/uvowelsigngujarati": "\u0AC1", + "/v": "\u0076", + "/vadeva": "\u0935", + "/vagujarati": "\u0AB5", + "/vagurmukhi": "\u0A35", + "/vakatakana": "\u30F7", + "/vanedownfunc": "\u2356", + "/vaneleftfunc": "\u2345", + "/vanerightfunc": "\u2346", + "/vaneupfunc": "\u234F", + "/varikajudeospanish:hb": "\uFB1E", + "/vav": "\u05D5", + "/vav:hb": "\u05D5", + "/vav_vav:hb": "\u05F0", + "/vav_yod:hb": "\u05F1", + "/vavdagesh": "\uFB35", + "/vavdagesh65": "\uFB35", + "/vavdageshhebrew": "\uFB35", + "/vavhebrew": "\u05D5", + "/vavholam": "\uFB4B", + "/vavholamhebrew": "\uFB4B", + "/vavvavhebrew": "\u05F0", + "/vavwithdagesh:hb": "\uFB35", + "/vavwithholam:hb": "\uFB4B", + "/vavyodhebrew": "\u05F1", + "/vcircle": "\u24E5", + "/vcurl": "\u2C74", + "/vdiagonalstroke": "\uA75F", + "/vdotbelow": "\u1E7F", + "/ve.fina": "\uFBDF", + "/ve.isol": "\uFBDE", + "/ve:abovetonecandra": "\u1CF4", + "/ve:anusvaraantargomukhasign": "\u1CE9", + "/ve:anusvarabahirgomukhasign": "\u1CEA", + "/ve:anusvarasignlong": "\u1CEF", + "/ve:anusvaraubhayatomukhasign": "\u1CF1", + "/ve:anusvaravamagomukhasign": "\u1CEB", + "/ve:anusvaravamagomukhawithtailsign": "\u1CEC", + "/ve:ardhavisargasign": "\u1CF2", + "/ve:atharvaindependentsvaritatone": "\u1CE1", + "/ve:atikramasign": "\u1CF7", + "/ve:belowtonecandra": "\u1CD8", + "/ve:dotbelowtone": "\u1CDD", + "/ve:hexiformanusvarasignlong": "\u1CEE", + "/ve:jihvamuliyasign": "\u1CF5", + "/ve:karshanatone": "\u1CD0", + "/ve:kathakaanudattatone": "\u1CDC", + "/ve:nihshvasasign": "\u1CD3", + "/ve:prenkhatone": "\u1CD2", + "/ve:rigkashmiriindependentsvaritatone": "\u1CE0", + "/ve:ringabovetone": "\u1CF8", + "/ve:ringabovetonedbl": "\u1CF9", + "/ve:rotatedardhavisargasign": "\u1CF3", + "/ve:rthanganusvarasignlong": "\u1CF0", + "/ve:sharatone": "\u1CD1", + "/ve:svaritatonedbl": "\u1CDA", + "/ve:svaritatonetpl": "\u1CDB", + "/ve:threedotsbelowtone": "\u1CDF", + "/ve:tiryaksign": "\u1CED", + "/ve:twodotsbelowtone": "\u1CDE", + "/ve:upadhmaniyasign": "\u1CF6", + "/ve:visargaanudattasign": "\u1CE5", + "/ve:visargaanudattasignreversed": "\u1CE6", + "/ve:visargaanudattawithtailsign": "\u1CE8", + "/ve:visargasvaritasign": "\u1CE2", + "/ve:visargaudattasign": "\u1CE3", + "/ve:visargaudattasignreversed": "\u1CE4", + "/ve:visargaudattawithtailsign": "\u1CE7", + "/ve:yajuraggravatedindependentsvaritatone": "\u1CD5", + "/ve:yajurindependentsvaritatone": "\u1CD6", + "/ve:yajurkathakaindependentsvaritaschroedertone": "\u1CD9", + "/ve:yajurkathakaindependentsvaritatone": "\u1CD7", + "/ve:yajurmidlinesvaritasign": "\u1CD4", + "/vecyr": "\u0432", + "/vecyrillic": "\u0432", + "/veh": "\u06A4", + "/veh.fina": "\uFB6B", + "/veh.init": "\uFB6C", + "/veh.isol": "\uFB6A", + "/veh.medi": "\uFB6D", + "/veharabic": "\u06A4", + "/vehfinalarabic": "\uFB6B", + "/vehinitialarabic": "\uFB6C", + "/vehmedialarabic": "\uFB6D", + "/vekatakana": "\u30F9", + "/vend": "\uA769", + "/venus": "\u2640", + "/versicle": "\u2123", + "/vert:bracketwhiteleft": "\uFE17", + "/vert:brakcetwhiteright": "\uFE18", + "/vert:colon": "\uFE13", + "/vert:comma": "\uFE10", + "/vert:ellipsishor": "\uFE19", + "/vert:exclam": "\uFE15", + "/vert:ideographiccomma": "\uFE11", + "/vert:ideographicfullstop": "\uFE12", + "/vert:question": "\uFE16", + "/vert:semicolon": "\uFE14", + "/vertdblhorzsng": "\u256B", + "/vertdblleftsng": "\u2562", + "/vertdblrightsng": "\u255F", + "/vertheavyhorzlight": "\u2542", + "/vertheavyleftlight": "\u2528", + "/vertheavyrightlight": "\u2520", + "/verticalTrafficLight": "\u1F6A6", + "/verticalbar": "\u007C", + "/verticalbardbl": "\u2016", + "/verticalbarhorizontalstroke": "\u27CA", + "/verticalbarwhitearrowonpedestalup": "\u21ED", + "/verticalfourdots": "\u205E", + "/verticalideographiciterationmark": "\u303B", + "/verticalkanarepeatmark": "\u3031", + "/verticalkanarepeatmarklowerhalf": "\u3035", + "/verticalkanarepeatmarkupperhalf": "\u3033", + "/verticalkanarepeatwithvoicedsoundmark": "\u3032", + "/verticalkanarepeatwithvoicedsoundmarkupperhalf": "\u3034", + "/verticallineabovecmb": "\u030D", + "/verticallinebelowcmb": "\u0329", + "/verticallinelowmod": "\u02CC", + "/verticallinemod": "\u02C8", + "/verticalmalestroke": "\u26A8", + "/verticalsdbltrokearrowleft": "\u21FA", + "/verticalsdbltrokearrowleftright": "\u21FC", + "/verticalsdbltrokearrowright": "\u21FB", + "/verticalstrokearrowleft": "\u21F7", + "/verticalstrokearrowleftright": "\u21F9", + "/verticalstrokearrowright": "\u21F8", + "/vertlighthorzheavy": "\u253F", + "/vertlightleftheavy": "\u2525", + "/vertlightrightheavy": "\u251D", + "/vertsnghorzdbl": "\u256A", + "/vertsngleftdbl": "\u2561", + "/vertsngrightdbl": "\u255E", + "/verymuchgreater": "\u22D9", + "/verymuchless": "\u22D8", + "/vesta": "\u26B6", + "/vewarmenian": "\u057E", + "/vhook": "\u028B", + "/vibrationMode": "\u1F4F3", + "/videoCamera": "\u1F4F9", + "/videoGame": "\u1F3AE", + "/videocassette": "\u1F4FC", + "/viewdatasquare": "\u2317", + "/vikatakana": "\u30F8", + "/violin": "\u1F3BB", + "/viramabengali": "\u09CD", + "/viramadeva": "\u094D", + "/viramagujarati": "\u0ACD", + "/virgo": "\u264D", + "/visargabengali": "\u0983", + "/visargadeva": "\u0903", + "/visargagujarati": "\u0A83", + "/visigothicz": "\uA763", + "/vmonospace": "\uFF56", + "/voarmenian": "\u0578", + "/vodsquare": "\u1F1AC", + "/voicediterationhiragana": "\u309E", + "/voicediterationkatakana": "\u30FE", + "/voicedmarkkana": "\u309B", + "/voicedmarkkanahalfwidth": "\uFF9E", + "/voicingmod": "\u02EC", + "/vokatakana": "\u30FA", + "/volapukae": "\uA79B", + "/volapukoe": "\uA79D", + "/volapukue": "\uA79F", + "/volcano": "\u1F30B", + "/volleyball": "\u1F3D0", + "/vovermfullwidth": "\u33DE", + "/vowelVabove": "\u065A", + "/voweldotbelow": "\u065C", + "/vowelinvertedVabove": "\u065B", + "/vparen": "\u24B1", + "/vparenthesized": "\u24B1", + "/vrighthook": "\u2C71", + "/vssquare": "\u1F19A", + "/vtilde": "\u1E7D", + "/vturned": "\u028C", + "/vuhiragana": "\u3094", + "/vukatakana": "\u30F4", + "/vwelsh": "\u1EFD", + "/vy": "\uA761", + "/w": "\u0077", + "/wacirclekatakana": "\u32FB", + "/wacute": "\u1E83", + "/waekorean": "\u3159", + "/wahiragana": "\u308F", + "/wakatakana": "\u30EF", + "/wakatakanahalfwidth": "\uFF9C", + "/wakorean": "\u3158", + "/waningCrescentMoon": "\u1F318", + "/waningGibbousMoon": "\u1F316", + "/warning": "\u26A0", + "/wasmallhiragana": "\u308E", + "/wasmallkatakana": "\u30EE", + "/wastebasket": "\u1F5D1", + "/watch": "\u231A", + "/waterBuffalo": "\u1F403", + "/waterCloset": "\u1F6BE", + "/waterWave": "\u1F30A", + "/waterideographiccircled": "\u328C", + "/waterideographicparen": "\u322C", + "/watermelon": "\u1F349", + "/wattosquare": "\u3357", + "/wavedash": "\u301C", + "/wavingBlackFlag": "\u1F3F4", + "/wavingHandSign": "\u1F44B", + "/wavingWhiteFlag": "\u1F3F3", + "/wavydash": "\u3030", + "/wavyhamzabelow": "\u065F", + "/wavyline": "\u2307", + "/wavyunderscorevertical": "\uFE34", + "/waw": "\u0648", + "/waw.fina": "\uFEEE", + "/waw.isol": "\uFEED", + "/wawDigitThreeAbove": "\u0779", + "/wawDigitTwoAbove": "\u0778", + "/wawarabic": "\u0648", + "/wawdotabove": "\u06CF", + "/wawfinalarabic": "\uFEEE", + "/wawhamza": "\u0624", + "/wawhamza.fina": "\uFE86", + "/wawhamza.isol": "\uFE85", + "/wawhamzaabovearabic": "\u0624", + "/wawhamzaabovefinalarabic": "\uFE86", + "/wawhighhamza": "\u0676", + "/wawring": "\u06C4", + "/wawsmall": "\u06E5", + "/wawtwodotsabove": "\u06CA", + "/waxingCrescentMoon": "\u1F312", + "/waxingGibbousMoon": "\u1F314", + "/wbfullwidth": "\u33DD", + "/wbsquare": "\u33DD", + "/wcircle": "\u24E6", + "/wcircumflex": "\u0175", + "/wcsquare": "\u1F14F", + "/wcsquareblack": "\u1F18F", + "/wdieresis": "\u1E85", + "/wdot": "\u1E87", + "/wdotaccent": "\u1E87", + "/wdotbelow": "\u1E89", + "/wearyCatFace": "\u1F640", + "/wearyFace": "\u1F629", + "/wecirclekatakana": "\u32FD", + "/wecyr": "\u051D", + "/wedding": "\u1F492", + "/wehiragana": "\u3091", + "/weierstrass": "\u2118", + "/weightLifter": "\u1F3CB", + "/wekatakana": "\u30F1", + "/wekorean": "\u315E", + "/weokorean": "\u315D", + "/westsyriaccross": "\u2670", + "/wgrave": "\u1E81", + "/whale": "\u1F40B", + "/wheelchair": "\u267F", + "/wheelofdharma": "\u2638", + "/whiteDownPointingBackhandIndex": "\u1F447", + "/whiteDownPointingLeftHandIndex": "\u1F597", + "/whiteFlower": "\u1F4AE", + "/whiteHardShellFloppyDisk": "\u1F5AB", + "/whiteLatinCross": "\u1F546", + "/whiteLeftPointingBackhandIndex": "\u1F448", + "/whitePennant": "\u1F3F1", + "/whiteRightPointingBackhandIndex": "\u1F449", + "/whiteSquareButton": "\u1F533", + "/whiteSun": "\u1F323", + "/whiteSunBehindCloud": "\u1F325", + "/whiteSunBehindCloudRain": "\u1F326", + "/whiteSunSmallCloud": "\u1F324", + "/whiteTouchtoneTelephone": "\u1F57E", + "/whiteUpPointingBackhandIndex": "\u1F446", + "/whitearrowdown": "\u21E9", + "/whitearrowfromwallright": "\u21F0", + "/whitearrowleft": "\u21E6", + "/whitearrowonpedestalup": "\u21EB", + "/whitearrowright": "\u21E8", + "/whitearrowup": "\u21E7", + "/whitearrowupdown": "\u21F3", + "/whitearrowupfrombar": "\u21EA", + "/whitebullet": "\u25E6", + "/whitecircle": "\u25CB", + "/whitecircleinverse": "\u25D9", + "/whitecornerbracketleft": "\u300E", + "/whitecornerbracketleftvertical": "\uFE43", + "/whitecornerbracketright": "\u300F", + "/whitecornerbracketrightvertical": "\uFE44", + "/whitedblarrowonpedestalup": "\u21EF", + "/whitedblarrowup": "\u21EE", + "/whitediamond": "\u25C7", + "/whitediamondcontainingblacksmalldiamond": "\u25C8", + "/whitedownpointingsmalltriangle": "\u25BF", + "/whitedownpointingtriangle": "\u25BD", + "/whiteleftpointingsmalltriangle": "\u25C3", + "/whiteleftpointingtriangle": "\u25C1", + "/whitelenticularbracketleft": "\u3016", + "/whitelenticularbracketright": "\u3017", + "/whiterightpointingsmalltriangle": "\u25B9", + "/whiterightpointingtriangle": "\u25B7", + "/whitesesamedot": "\uFE46", + "/whitesmallsquare": "\u25AB", + "/whitesmilingface": "\u263A", + "/whitesquare": "\u25A1", + "/whitesquarebracketleft": "\u301A", + "/whitesquarebracketright": "\u301B", + "/whitestar": "\u2606", + "/whitetelephone": "\u260F", + "/whitetortoiseshellbracketleft": "\u3018", + "/whitetortoiseshellbracketright": "\u3019", + "/whiteuppointingsmalltriangle": "\u25B5", + "/whiteuppointingtriangle": "\u25B3", + "/whook": "\u2C73", + "/wicirclekatakana": "\u32FC", + "/wigglylinevertical": "\u2E3E", + "/wignyan": "\uA983", + "/wihiragana": "\u3090", + "/wikatakana": "\u30F0", + "/wikorean": "\u315F", + "/windBlowingFace": "\u1F32C", + "/windChime": "\u1F390", + "/windupada": "\uA9C6", + "/wineGlass": "\u1F377", + "/winkingFace": "\u1F609", + "/wiredKeyboard": "\u1F5AE", + "/wmonospace": "\uFF57", + "/wocirclekatakana": "\u32FE", + "/wohiragana": "\u3092", + "/wokatakana": "\u30F2", + "/wokatakanahalfwidth": "\uFF66", + "/wolfFace": "\u1F43A", + "/woman": "\u1F469", + "/womanBunnyEars": "\u1F46F", + "/womansBoots": "\u1F462", + "/womansClothes": "\u1F45A", + "/womansHat": "\u1F452", + "/womansSandal": "\u1F461", + "/womens": "\u1F6BA", + "/won": "\u20A9", + "/wonmonospace": "\uFFE6", + "/woodideographiccircled": "\u328D", + "/woodideographicparen": "\u322D", + "/wordjoiner": "\u2060", + "/wordseparatormiddledot": "\u2E31", + "/worldMap": "\u1F5FA", + "/worriedFace": "\u1F61F", + "/wowaenthai": "\u0E27", + "/wparen": "\u24B2", + "/wparenthesized": "\u24B2", + "/wrappedPresent": "\u1F381", + "/wreathproduct": "\u2240", + "/wrench": "\u1F527", + "/wring": "\u1E98", + "/wsuperior": "\u02B7", + "/wsupmod": "\u02B7", + "/wturned": "\u028D", + "/wulumelikvowel": "\uA9B7", + "/wuluvowel": "\uA9B6", + "/wynn": "\u01BF", + "/x": "\u0078", + "/x.inferior": "\u2093", + "/xabovecmb": "\u033D", + "/xatailcyr": "\u04B3", + "/xbopomofo": "\u3112", + "/xcircle": "\u24E7", + "/xdieresis": "\u1E8D", + "/xdot": "\u1E8B", + "/xdotaccent": "\u1E8B", + "/xeharmenian": "\u056D", + "/xi": "\u03BE", + "/xmonospace": "\uFF58", + "/xor": "\u22BB", + "/xparen": "\u24B3", + "/xparenthesized": "\u24B3", + "/xsuperior": "\u02E3", + "/xsupmod": "\u02E3", + "/y": "\u0079", + "/yaadosquare": "\u334E", + "/yaarusquare": "\u334F", + "/yabengali": "\u09AF", + "/yacirclekatakana": "\u32F3", + "/yacute": "\u00FD", + "/yacyr": "\u044F", + "/yadeva": "\u092F", + "/yaecyr": "\u0519", + "/yaekorean": "\u3152", + "/yagujarati": "\u0AAF", + "/yagurmukhi": "\u0A2F", + "/yahiragana": "\u3084", + "/yakatakana": "\u30E4", + "/yakatakanahalfwidth": "\uFF94", + "/yakorean": "\u3151", + "/yamakkanthai": "\u0E4E", + "/yangtonemod": "\u02EB", + "/yasmallhiragana": "\u3083", + "/yasmallkatakana": "\u30E3", + "/yasmallkatakanahalfwidth": "\uFF6C", + "/yatcyr": "\u0463", + "/yatcyrillic": "\u0463", + "/ycircle": "\u24E8", + "/ycircumflex": "\u0177", + "/ydieresis": "\u00FF", + "/ydot": "\u1E8F", + "/ydotaccent": "\u1E8F", + "/ydotbelow": "\u1EF5", + "/yeh": "\u064A", + "/yeh.fina": "\uFEF2", + "/yeh.init": "\uFEF3", + "/yeh.init_alefmaksura.fina": "\uFC59", + "/yeh.init_hah.fina": "\uFC56", + "/yeh.init_hah.medi": "\uFCDB", + "/yeh.init_hamzaabove.medi_ae.fina": "\uFBEC", + "/yeh.init_hamzaabove.medi_alef.fina": "\uFBEA", + "/yeh.init_hamzaabove.medi_alefmaksura.fina": "\uFC03", + "/yeh.init_hamzaabove.medi_e.fina": "\uFBF6", + "/yeh.init_hamzaabove.medi_e.medi": "\uFBF8", + "/yeh.init_hamzaabove.medi_hah.fina": "\uFC01", + "/yeh.init_hamzaabove.medi_hah.medi": "\uFC98", + "/yeh.init_hamzaabove.medi_heh.medi": "\uFC9B", + "/yeh.init_hamzaabove.medi_jeem.fina": "\uFC00", + "/yeh.init_hamzaabove.medi_jeem.medi": "\uFC97", + "/yeh.init_hamzaabove.medi_khah.medi": "\uFC99", + "/yeh.init_hamzaabove.medi_meem.fina": "\uFC02", + "/yeh.init_hamzaabove.medi_meem.medi": "\uFC9A", + "/yeh.init_hamzaabove.medi_oe.fina": "\uFBF2", + "/yeh.init_hamzaabove.medi_u.fina": "\uFBF0", + "/yeh.init_hamzaabove.medi_waw.fina": "\uFBEE", + "/yeh.init_hamzaabove.medi_yeh.fina": "\uFC04", + "/yeh.init_hamzaabove.medi_yu.fina": "\uFBF4", + "/yeh.init_heh.medi": "\uFCDE", + "/yeh.init_jeem.fina": "\uFC55", + "/yeh.init_jeem.medi": "\uFCDA", + "/yeh.init_khah.fina": "\uFC57", + "/yeh.init_khah.medi": "\uFCDC", + "/yeh.init_meem.fina": "\uFC58", + "/yeh.init_meem.medi": "\uFCDD", + "/yeh.init_meem.medi_meem.medi": "\uFD9D", + "/yeh.init_yeh.fina": "\uFC5A", + "/yeh.isol": "\uFEF1", + "/yeh.medi": "\uFEF4", + "/yeh.medi_alefmaksura.fina": "\uFC95", + "/yeh.medi_hah.medi_yeh.fina": "\uFDAE", + "/yeh.medi_hamzaabove.medi_ae.fina": "\uFBED", + "/yeh.medi_hamzaabove.medi_alef.fina": "\uFBEB", + "/yeh.medi_hamzaabove.medi_alefmaksura.fina": "\uFC68", + "/yeh.medi_hamzaabove.medi_e.fina": "\uFBF7", + "/yeh.medi_hamzaabove.medi_heh.medi": "\uFCE0", + "/yeh.medi_hamzaabove.medi_meem.fina": "\uFC66", + "/yeh.medi_hamzaabove.medi_meem.medi": "\uFCDF", + "/yeh.medi_hamzaabove.medi_noon.fina": "\uFC67", + "/yeh.medi_hamzaabove.medi_oe.fina": "\uFBF3", + "/yeh.medi_hamzaabove.medi_reh.fina": "\uFC64", + "/yeh.medi_hamzaabove.medi_u.fina": "\uFBF1", + "/yeh.medi_hamzaabove.medi_waw.fina": "\uFBEF", + "/yeh.medi_hamzaabove.medi_yeh.fina": "\uFC69", + "/yeh.medi_hamzaabove.medi_yu.fina": "\uFBF5", + "/yeh.medi_hamzaabove.medi_zain.fina": "\uFC65", + "/yeh.medi_heh.medi": "\uFCF1", + "/yeh.medi_jeem.medi_yeh.fina": "\uFDAF", + "/yeh.medi_meem.fina": "\uFC93", + "/yeh.medi_meem.medi": "\uFCF0", + "/yeh.medi_meem.medi_meem.fina": "\uFD9C", + "/yeh.medi_meem.medi_yeh.fina": "\uFDB0", + "/yeh.medi_noon.fina": "\uFC94", + "/yeh.medi_reh.fina": "\uFC91", + "/yeh.medi_yeh.fina": "\uFC96", + "/yeh.medi_zain.fina": "\uFC92", + "/yehBarreeDigitThreeAbove": "\u077B", + "/yehBarreeDigitTwoAbove": "\u077A", + "/yehVabove": "\u06CE", + "/yehabove": "\u06E7", + "/yeharabic": "\u064A", + "/yehbarree": "\u06D2", + "/yehbarree.fina": "\uFBAF", + "/yehbarree.isol": "\uFBAE", + "/yehbarreearabic": "\u06D2", + "/yehbarreefinalarabic": "\uFBAF", + "/yehbarreehamza": "\u06D3", + "/yehbarreehamza.fina": "\uFBB1", + "/yehbarreehamza.isol": "\uFBB0", + "/yehfarsi": "\u06CC", + "/yehfarsi.fina": "\uFBFD", + "/yehfarsi.init": "\uFBFE", + "/yehfarsi.isol": "\uFBFC", + "/yehfarsi.medi": "\uFBFF", + "/yehfarsiinvertedV": "\u063D", + "/yehfarsithreedotsabove": "\u063F", + "/yehfarsitwodotsabove": "\u063E", + "/yehfinalarabic": "\uFEF2", + "/yehhamza": "\u0626", + "/yehhamza.fina": "\uFE8A", + "/yehhamza.init": "\uFE8B", + "/yehhamza.isol": "\uFE89", + "/yehhamza.medi": "\uFE8C", + "/yehhamzaabovearabic": "\u0626", + "/yehhamzaabovefinalarabic": "\uFE8A", + "/yehhamzaaboveinitialarabic": "\uFE8B", + "/yehhamzaabovemedialarabic": "\uFE8C", + "/yehhighhamza": "\u0678", + "/yehinitialarabic": "\uFEF3", + "/yehmedialarabic": "\uFEF4", + "/yehmeeminitialarabic": "\uFCDD", + "/yehmeemisolatedarabic": "\uFC58", + "/yehnoonfinalarabic": "\uFC94", + "/yehsmall": "\u06E6", + "/yehtail": "\u06CD", + "/yehthreedotsbelow": "\u06D1", + "/yehthreedotsbelowarabic": "\u06D1", + "/yekorean": "\u3156", + "/yellowHeart": "\u1F49B", + "/yen": "\u00A5", + "/yenmonospace": "\uFFE5", + "/yeokorean": "\u3155", + "/yeorinhieuhkorean": "\u3186", + "/yerachBenYomo:hb": "\u05AA", + "/yerahbenyomohebrew": "\u05AA", + "/yerahbenyomolefthebrew": "\u05AA", + "/yericyrillic": "\u044B", + "/yerudieresiscyrillic": "\u04F9", + "/yesieungkorean": "\u3181", + "/yesieungpansioskorean": "\u3183", + "/yesieungsioskorean": "\u3182", + "/yetiv:hb": "\u059A", + "/yetivhebrew": "\u059A", + "/ygrave": "\u1EF3", + "/yhoi": "\u1EF7", + "/yhook": "\u01B4", + "/yhookabove": "\u1EF7", + "/yiarmenian": "\u0575", + "/yicyrillic": "\u0457", + "/yikorean": "\u3162", + "/yintonemod": "\u02EA", + "/yinyang": "\u262F", + "/yiwnarmenian": "\u0582", + "/ylongcyr": "\u044B", + "/ylongdieresiscyr": "\u04F9", + "/yloop": "\u1EFF", + "/ymacron": "\u0233", + "/ymonospace": "\uFF59", + "/yocirclekatakana": "\u32F5", + "/yod": "\u05D9", + "/yod:hb": "\u05D9", + "/yod_yod:hb": "\u05F2", + "/yod_yod_patah:hb": "\uFB1F", + "/yoddagesh": "\uFB39", + "/yoddageshhebrew": "\uFB39", + "/yodhebrew": "\u05D9", + "/yodwithdagesh:hb": "\uFB39", + "/yodwithhiriq:hb": "\uFB1D", + "/yodyodhebrew": "\u05F2", + "/yodyodpatahhebrew": "\uFB1F", + "/yogh": "\u021D", + "/yohiragana": "\u3088", + "/yoikorean": "\u3189", + "/yokatakana": "\u30E8", + "/yokatakanahalfwidth": "\uFF96", + "/yokorean": "\u315B", + "/yosmallhiragana": "\u3087", + "/yosmallkatakana": "\u30E7", + "/yosmallkatakanahalfwidth": "\uFF6E", + "/yot": "\u03F3", + "/yotgreek": "\u03F3", + "/yoyaekorean": "\u3188", + "/yoyakorean": "\u3187", + "/yoyakthai": "\u0E22", + "/yoyingthai": "\u0E0D", + "/yparen": "\u24B4", + "/yparenthesized": "\u24B4", + "/ypogegrammeni": "\u037A", + "/ypogegrammenigreekcmb": "\u0345", + "/yr": "\u01A6", + "/yring": "\u1E99", + "/ystroke": "\u024F", + "/ysuperior": "\u02B8", + "/ysupmod": "\u02B8", + "/ytilde": "\u1EF9", + "/yturned": "\u028E", + "/yu.fina": "\uFBDC", + "/yu.isol": "\uFBDB", + "/yuansquare": "\u3350", + "/yucirclekatakana": "\u32F4", + "/yucyr": "\u044E", + "/yuhiragana": "\u3086", + "/yuikorean": "\u318C", + "/yukatakana": "\u30E6", + "/yukatakanahalfwidth": "\uFF95", + "/yukirghiz": "\u06C9", + "/yukirghiz.fina": "\uFBE3", + "/yukirghiz.isol": "\uFBE2", + "/yukorean": "\u3160", + "/yukrcyr": "\u0457", + "/yusbigcyr": "\u046B", + "/yusbigcyrillic": "\u046B", + "/yusbigiotifiedcyr": "\u046D", + "/yusbigiotifiedcyrillic": "\u046D", + "/yuslittlecyr": "\u0467", + "/yuslittlecyrillic": "\u0467", + "/yuslittleiotifiedcyr": "\u0469", + "/yuslittleiotifiedcyrillic": "\u0469", + "/yusmallhiragana": "\u3085", + "/yusmallkatakana": "\u30E5", + "/yusmallkatakanahalfwidth": "\uFF6D", + "/yuyekorean": "\u318B", + "/yuyeokorean": "\u318A", + "/yyabengali": "\u09DF", + "/yyadeva": "\u095F", + "/z": "\u007A", + "/zaarmenian": "\u0566", + "/zacute": "\u017A", + "/zadeva": "\u095B", + "/zagurmukhi": "\u0A5B", + "/zah": "\u0638", + "/zah.fina": "\uFEC6", + "/zah.init": "\uFEC7", + "/zah.init_meem.fina": "\uFC28", + "/zah.init_meem.medi": "\uFCB9", + "/zah.isol": "\uFEC5", + "/zah.medi": "\uFEC8", + "/zah.medi_meem.medi": "\uFD3B", + "/zaharabic": "\u0638", + "/zahfinalarabic": "\uFEC6", + "/zahinitialarabic": "\uFEC7", + "/zahiragana": "\u3056", + "/zahmedialarabic": "\uFEC8", + "/zain": "\u0632", + "/zain.fina": "\uFEB0", + "/zain.isol": "\uFEAF", + "/zainabove": "\u0617", + "/zainarabic": "\u0632", + "/zainfinalarabic": "\uFEB0", + "/zakatakana": "\u30B6", + "/zaqefGadol:hb": "\u0595", + "/zaqefQatan:hb": "\u0594", + "/zaqefgadolhebrew": "\u0595", + "/zaqefqatanhebrew": "\u0594", + "/zarqa:hb": "\u0598", + "/zarqahebrew": "\u0598", + "/zayin": "\u05D6", + "/zayin:hb": "\u05D6", + "/zayindagesh": "\uFB36", + "/zayindageshhebrew": "\uFB36", + "/zayinhebrew": "\u05D6", + "/zayinwithdagesh:hb": "\uFB36", + "/zbopomofo": "\u3117", + "/zcaron": "\u017E", + "/zcircle": "\u24E9", + "/zcircumflex": "\u1E91", + "/zcurl": "\u0291", + "/zdescender": "\u2C6C", + "/zdot": "\u017C", + "/zdotaccent": "\u017C", + "/zdotbelow": "\u1E93", + "/zecyr": "\u0437", + "/zecyrillic": "\u0437", + "/zedescendercyrillic": "\u0499", + "/zedieresiscyr": "\u04DF", + "/zedieresiscyrillic": "\u04DF", + "/zehiragana": "\u305C", + "/zekatakana": "\u30BC", + "/zero": "\u0030", + "/zero.inferior": "\u2080", + "/zero.superior": "\u2070", + "/zeroarabic": "\u0660", + "/zerobengali": "\u09E6", + "/zerocircle": "\u24EA", + "/zerocircleblack": "\u24FF", + "/zerocomma": "\u1F101", + "/zerodeva": "\u0966", + "/zerofar": "\u06F0", + "/zerofullstop": "\u1F100", + "/zerogujarati": "\u0AE6", + "/zerogurmukhi": "\u0A66", + "/zerohackarabic": "\u0660", + "/zeroinferior": "\u2080", + "/zeromonospace": "\uFF10", + "/zerooldstyle": "\uF730", + "/zeropersian": "\u06F0", + "/zerosquareabove": "\u06E0", + "/zerosuperior": "\u2070", + "/zerothai": "\u0E50", + "/zerothirds": "\u2189", + "/zerowidthjoiner": "\uFEFF", + "/zerowidthnobreakspace": "\uFEFF", + "/zerowidthnonjoiner": "\u200C", + "/zerowidthspace": "\u200B", + "/zeta": "\u03B6", + "/zetailcyr": "\u0499", + "/zhbopomofo": "\u3113", + "/zhearmenian": "\u056A", + "/zhebrevecyr": "\u04C2", + "/zhebrevecyrillic": "\u04C2", + "/zhecyr": "\u0436", + "/zhecyrillic": "\u0436", + "/zhedescendercyrillic": "\u0497", + "/zhedieresiscyr": "\u04DD", + "/zhedieresiscyrillic": "\u04DD", + "/zhetailcyr": "\u0497", + "/zhook": "\u0225", + "/zihiragana": "\u3058", + "/zikatakana": "\u30B8", + "/zildefunc": "\u236C", + "/zinorhebrew": "\u05AE", + "/zjekomicyr": "\u0505", + "/zlinebelow": "\u1E95", + "/zmonospace": "\uFF5A", + "/znotationbagmembership": "\u22FF", + "/zohiragana": "\u305E", + "/zokatakana": "\u30BE", + "/zparen": "\u24B5", + "/zparenthesized": "\u24B5", + "/zretroflex": "\u0290", + "/zretroflexhook": "\u0290", + "/zstroke": "\u01B6", + "/zswashtail": "\u0240", + "/zuhiragana": "\u305A", + "/zukatakana": "\u30BA", + "/zwarakay": "\u0659", + # manually added from + # https://github.com/serviceprototypinglab/latex-pdfa/blob/master/glyphtounicode-cmr.tex + "/angbracketleftBig": "\u28E8", + "/angbracketleftBigg": "\u27E8", + "/angbracketleftbig": "\u27E8", + "/angbracketleftbigg": "\u27E8", + "/angbracketrightBig": "\u27E9", + "/angbracketrightBigg": "\u27E9", + "/angbracketrightbig": "\u27E9", + "/angbracketrightbigg": "\u27E9", + "/arrowbt": "\u2193", + "/arrowdblbt": "\u21D3", + "/arrowdbltp": "\u21D1", + "/arrowhookleft": "\u21AA", + "/arrowhookright": "\u21A9", + "/arrowtp": "\u2191", + # diff : "/arrowvertex": "\u23D0", + "/arrowvertexdbl": "\uED12", + "/backslashBig": "\u005C", + "/backslashBigg": "\u005C", + "/backslashbig": "\u005C", + "/backslashbigg": "\u005C", + # diff : "/braceex": "\u23AA", + "/bracehtipdownleft": "\uED17", + "/bracehtipdownright": "\uED18", + "/bracehtipupleft": "\uED19", + "/bracehtipupright": "\uED1A", + "/braceleftBig": "\u007B", + "/braceleftBigg": "\u007B", + "/braceleftbig": "\u007B", + "/braceleftbigg": "\u007B", + # diff : "/braceleftbt": "\u23A9", + # diff : "/braceleftmid": "\u23A8", + # diff : "/bracelefttp": "\u23A7", + "/bracerightBig": "\u007D", + "/bracerightBigg": "\u007D", + "/bracerightbig": "\u007D", + "/bracerightbigg": "\u007D", + # diff : "/bracerightbt": "\u23AD", + # diff : "/bracerightmid": "\u23AC", + # diff : "/bracerighttp": "\u23AB", + "/bracketleftBig": "\u005B", + "/bracketleftBigg": "\u005B", + "/bracketleftbig": "\u005B", + "/bracketleftbigg": "\u005B", + # diff : "/bracketleftbt": "\u23A3", + # diff : "/bracketleftex": "\u23A2", + # diff : "/bracketlefttp": "\u23A1", + "/bracketrightBig": "\u005D", + "/bracketrightBigg": "\u005D", + "/bracketrightbig": "\u005D", + "/bracketrightbigg": "\u005D", + # diff : "/bracketrightbt": "\u23A6", + # diff : "/bracketrightex": "\u23A5", + # diff : "/bracketrighttp": "\u23A4", + "/ceilingleftBig": "\u2308", + "/ceilingleftBigg": "\u2308", + "/ceilingleftbig": "\u2308", + "/ceilingleftbigg": "\u2308", + "/ceilingrightBig": "\u2309", + "/ceilingrightBigg": "\u2309", + "/ceilingrightbig": "\u2309", + "/ceilingrightbigg": "\u2309", + "/circledotdisplay": "\u2A00", + "/circledottext": "\u2A00", + "/circlemultiplydisplay": "\u2A02", + "/circlemultiplytext": "\u2A02", + "/circleplusdisplay": "\u2A01", + "/circleplustext": "\u2A01", + "/contintegraldisplay": "\u222E", + "/contintegraltext": "\u222E", + "/coproductdisplay": "\u2210", + "/coproducttext": "\u2210", + "/floorleftBig": "\u230A", + "/floorleftBigg": "\u230A", + "/floorleftbig": "\u230A", + "/floorleftbigg": "\u230A", + "/floorrightBig": "\u230B", + "/floorrightBigg": "\u230B", + "/floorrightbig": "\u230B", + "/floorrightbigg": "\u230B", + "/hatwide": "\u02C6", + "/hatwider": "\u02C6", + "/hatwidest": "\u02C6", + "/integraldisplay": "\u222B", + "/integraltext": "\u222B", + "/intersectiondisplay": "\u22C2", + "/intersectiontext": "\u22C2", + "/logicalanddisplay": "\u22C0", + "/logicalandtext": "\u22C0", + "/logicalordisplay": "\u22C1", + "/logicalortext": "\u22C1", + "/mapsto": "\u21A6", + "/parenleftBig": "\u0028", + "/parenleftBigg": "\u0028", + "/parenleftbig": "\u0028", + "/parenleftbigg": "\u0028", + # diff : "/parenleftbt": "\u239D", + # diff : "/parenleftex": "\u239C", + # diff : "/parenlefttp": "\u239B", + "/parenrightBig": "\u0029", + "/parenrightBigg": "\u0029", + "/parenrightbig": "\u0029", + "/parenrightbigg": "\u0029", + # diff : "/parenrightbt": "\u23A0", + # diff : "/parenrightex": "\u239F", + # diff : "/parenrighttp": "\u239E", + "/productdisplay": "\u220F", + "/producttext": "\u220F", + "/radicalBig": "\u221A", + "/radicalBigg": "\u221A", + "/radicalbig": "\u221A", + "/radicalbigg": "\u221A", + "/radicalbt": "\u221A", + "/radicaltp": "\uED6A", + "/radicalvertex": "\uED6B", + "/slashBig": "\u002F", + "/slashBigg": "\u002F", + "/slashbig": "\u002F", + "/slashbigg": "\u002F", + "/summationdisplay": "\u2211", + "/summationtext": "\u2211", + "/tie": "\u2040", + "/tildewide": "\u02DC", + "/tildewider": "\u02DC", + "/tildewidest": "\u02DC", + "/uniondisplay": "\u22C3", + "/unionmultidisplay": "\u2A04", + "/unionmultitext": "\u2A04", + "/unionsqdisplay": "\u2A06", + "/unionsqtext": "\u2A06", + "/uniontext": "\u22C3", + "/vextenddouble": "\uED79", + "/vextendsingle": "\u23D0", + "/a1": "\u25C1", + "/a2": "\u22B4", + "/a3": "\u25B7", + "/a4": "\u22B5", + "/a40": "\u02C2", + "/a41": "\u02C3", + "/a42": "\u2303", + "/a43": "\u2304", + "/a48": "\u2127", + "/a49": "\u22C8", + "/a50": "\u25A1", + "/a51": "\u25C7", + "/a58": "\u2053", + "/a59": "\u219D", + "/a60": "\u228F", + "/a61": "\u2290", + "/d0": "\u2199", + "/d1": "\u2199", + "/d2": "\u2199", + "/d3": "\u2199", + "/d4": "\u2199", + "/d5": "\u2199", + "/d6": "\u2199", + "/d7": "\u2193", + "/d8": "\u2193", + "/d9": "\u2193", + "/d10": "\u2193", + "/d11": "\u2193", + "/d12": "\u2193", + "/d13": "\u2193", + "/d14": "\u2193", + "/d15": "\u2193", + "/d16": "\u2193", + "/d17": "\u2193", + "/d18": "\u2193", + "/d19": "\u2193", + "/d20": "\u2193", + "/d21": "\u2193", + "/d22": "\u2193", + "/d23": "\u2193", + "/d24": "\u2198", + "/d25": "\u2198", + "/d26": "\u2198", + "/d27": "\u2198", + "/d28": "\u2198", + "/d29": "\u2198", + "/d30": "\u2198", + "/d31": "\u2198", + "/d32": "\u2198", + "/d33": "\u2198", + "/d34": "\u2198", + "/d35": "\u2198", + "/d36": "\u2198", + "/d37": "\u2198", + "/d38": "\u2198", + "/d39": "\u2192", + "/d40": "\u2192", + "/d41": "\u2192", + "/d42": "\u2192", + "/d43": "\u2192", + "/d44": "\u2192", + "/d45": "\u2192", + "/d46": "\u2192", + "/d47": "\u2192", + "/d48": "\u2192", + "/d49": "\u2192", + "/d50": "\u2192", + "/d51": "\u2192", + "/d52": "\u2192", + "/d53": "\u2192", + "/d54": "\u2192", + "/d55": "\u2192", + "/d56": "\u2197", + "/d57": "\u2197", + "/d58": "\u2197", + "/d59": "\u2197", + "/d60": "\u2197", + "/d61": "\u2197", + "/d62": "\u2197", + "/d63": "\u2197", + "/d64": "\u2197", + "/d65": "\u2197", + "/d66": "\u2197", + "/d67": "\u2197", + "/d68": "\u2197", + "/d69": "\u2197", + "/d70": "\u2197", + "/d71": "\u2191", + "/d72": "\u2191", + "/d73": "\u2191", + "/d74": "\u2191", + "/d75": "\u2191", + "/d76": "\u2191", + "/d77": "\u2191", + "/d78": "\u2191", + "/d79": "\u2191", + "/d80": "\u2191", + "/d81": "\u2191", + "/d82": "\u2191", + "/d83": "\u2191", + "/d84": "\u2191", + "/d85": "\u2191", + "/d86": "\u2191", + "/d87": "\u2191", + "/d88": "\u2196", + "/d89": "\u2196", + "/d90": "\u2196", + "/d91": "\u2196", + "/d92": "\u2196", + "/d93": "\u2196", + "/d94": "\u2196", + "/d95": "\u2196", + "/d96": "\u2196", + "/d97": "\u2196", + "/d98": "\u2196", + "/d99": "\u2196", + "/d100": "\u2196", + "/d101": "\u2196", + "/d102": "\u2196", + "/d103": "\u2190", + "/d104": "\u2190", + "/d105": "\u2190", + "/d106": "\u2190", + "/d107": "\u2190", + "/d108": "\u2190", + "/d109": "\u2190", + "/d110": "\u2190", + "/d111": "\u2190", + "/d112": "\u2190", + "/d113": "\u2190", + "/d114": "\u2190", + "/d115": "\u2190", + "/d116": "\u2190", + "/d117": "\u2190", + "/d118": "\u2190", + "/d119": "\u2190", + "/d120": "\u2199", + "/d121": "\u2199", + "/d122": "\u2199", + "/d123": "\u2199", + "/d124": "\u2199", + "/d125": "\u2199", + "/d126": "\u2199", + "/d127": "\u2199", + # manually added from + # https://github.com/kohler/lcdf-typetools/blob/master/texglyphlist.txt + "/Ifractur": "\u2111", + "/FFsmall": "\uF766", + "/FFIsmall": "\uF766", + "/FFLsmall": "\uF766", + "/FIsmall": "\uF766", + "/FLsmall": "\uF766", + # diff : "/Germandbls": "\u0053", + "/Germandblssmall": "\uF773", + "/Ng": "\u014A", + "/Rfractur": "\u211C", + "/SS": "\u0053", + "/SSsmall": "\uF773", + "/altselector": "\uD802", + "/angbracketleft": "\u27E8", + "/angbracketright": "\u27E9", + "/arrowbothv": "\u2195", + "/arrowdblbothv": "\u21D5", + "/arrowleftbothalf": "\u21BD", + "/arrowlefttophalf": "\u21BC", + "/arrownortheast": "\u2197", + "/arrownorthwest": "\u2196", + "/arrowrightbothalf": "\u21C1", + "/arrowrighttophalf": "\u21C0", + "/arrowsoutheast": "\u2198", + "/arrowsouthwest": "\u2199", + "/ascendercompwordmark": "\uD80A", + "/asteriskcentered": "\u2217", + "/bardbl": "\u2225", + "/capitalcompwordmark": "\uD809", + "/circlecopyrt": "\u20DD", + "/circledivide": "\u2298", + "/circleminus": "\u2296", + "/coproduct": "\u2A3F", + "/ct": "\u0063", + "/cwm": "\u200C", + "/dblbracketleft": "\u27E6", + "/dblbracketright": "\u27E7", + # diff : "/diamond": "\u2662", + "/diamondmath": "\u22C4", + # diff : "/dotlessj": "\u0237", + "/emptyslot": "\uD801", + "/epsilon1": "\u03F5", + "/epsiloninv": "\u03F6", + "/equivasymptotic": "\u224D", + "/flat": "\u266D", + "/follows": "\u227B", + "/followsequal": "\u2AB0", + "/followsorcurly": "\u227D", + "/greatermuch": "\u226B", + # diff : "/heart": "\u2661", + "/interrobangdown": "\u2E18", + "/intersectionsq": "\u2293", + "/latticetop": "\u22A4", + "/lessmuch": "\u226A", + "/longdbls": "\u017F", + "/longsh": "\u017F", + "/longsi": "\u017F", + "/longsl": "\u017F", + "/longst": "\uFB05", + "/lscript": "\u2113", + "/natural": "\u266E", + "/negationslash": "\u0338", + "/ng": "\u014B", + "/owner": "\u220B", + "/pertenthousand": "\u2031", + # diff : "/phi": "\u03D5", + # diff : "/phi1": "\u03C6", + "/pi1": "\u03D6", + "/precedesequal": "\u2AAF", + "/precedesorcurly": "\u227C", + "/prime": "\u2032", + "/rho1": "\u03F1", + "/ringfitted": "\uD80D", + "/sharp": "\u266F", + "/similarequal": "\u2243", + "/slurabove": "\u2322", + "/slurbelow": "\u2323", + "/st": "\uFB06", + "/subsetsqequal": "\u2291", + "/supersetsqequal": "\u2292", + "/triangle": "\u25B3", + "/triangleinv": "\u25BD", + "/triangleleft": "\u25C1", + # diff : "/triangleright": "\u25B7", + "/turnstileleft": "\u22A2", + "/turnstileright": "\u22A3", + "/twelveudash": "\uD80C", + "/unionmulti": "\u228E", + "/unionsq": "\u2294", + "/vector": "\u20D7", + "/visualspace": "\u2423", + "/Dbar": "\u0110", + "/compwordmark": "\u200C", + "/dbar": "\u0111", + "/rangedash": "\u2013", + "/hyphenchar": "\u002D", + "/punctdash": "\u2014", + "/visiblespace": "\u2423", + "/Yen": "\u00A5", + "/anticlockwise": "\u27F2", + "/arrowparrleftright": "\u21C6", + "/arrowparrrightleft": "\u21C4", + "/arrowtailleft": "\u21A2", + "/arrowtailright": "\u21A3", + "/arrowtripleleft": "\u21DA", + "/arrowtripleright": "\u21DB", + "/check": "\u2713", + "/circleR": "\u00AE", + "/circleS": "\u24C8", + "/circleasterisk": "\u229B", + "/circleequal": "\u229C", + "/circlering": "\u229A", + "/clockwise": "\u27F3", + "/curlyleft": "\u21AB", + "/curlyright": "\u21AC", + "/dblarrowdwn": "\u21CA", + "/dblarrowheadleft": "\u219E", + "/dblarrowheadright": "\u21A0", + # diff : "/dblarrowup": "\u21C8", + "/defines": "\u225C", + "/diamondsolid": "\u2666", + "/difference": "\u224F", + "/downfall": "\u22CE", + "/equaldotleftright": "\u2252", + "/equaldotrightleft": "\u2253", + "/equalorfollows": "\u22DF", + # diff : "/equalorgreater": "\u2A96", + # diff : "/equalorless": "\u2A95", + "/equalsdots": "\u2251", + "/followsorequal": "\u227F", + "/forcesbar": "\u22AA", + # diff : "/fork": "\u22D4", + "/geomequivalent": "\u224E", + "/greaterdbleqlless": "\u2A8C", + "/greaterdblequal": "\u2267", + "/greaterlessequal": "\u22DB", + "/greaterorapproxeql": "\u2A86", + "/greaterorequalslant": "\u2A7E", + "/greaterorsimilar": "\u2273", + "/harpoondownleft": "\u21C3", + "/harpoondownright": "\u21C2", + "/harpoonleftright": "\u21CC", + "/harpoonrightleft": "\u21CB", + "/harpoonupleft": "\u21BF", + "/harpoonupright": "\u21BE", + "/intercal": "\u22BA", + "/lessdbleqlgreater": "\u2A8B", + "/lessdblequal": "\u2266", + "/lessequalgreater": "\u22DA", + "/lessorapproxeql": "\u2A85", + "/lessorequalslant": "\u2A7D", + "/lessorsimilar": "\u2272", + "/maltesecross": "\u2720", + "/multiopenleft": "\u22CB", + "/multiopenright": "\u22CC", + "/orunderscore": "\u22BB", + "/perpcorrespond": "\u2A5E", + # diff : "/precedesorequal": "\u227E", + "/primereverse": "\u2035", + "/revasymptequal": "\u22CD", + "/revsimilar": "\u223D", + "/rightanglene": "\u231D", + "/rightanglenw": "\u231C", + "/rightanglese": "\u231F", + "/rightanglesw": "\u231E", + "/satisfies": "\u22A8", + "/shiftleft": "\u21B0", + "/shiftright": "\u21B1", + "/square": "\u25A1", + "/squaredot": "\u22A1", + "/squareminus": "\u229F", + "/squaremultiply": "\u22A0", + "/squareplus": "\u229E", + "/squaresolid": "\u25A0", + "/squiggleleftright": "\u21AD", + "/squiggleright": "\u21DD", + "/subsetdblequal": "\u2AC5", + "/supersetdbl": "\u22D1", + "/supersetdblequal": "\u2AC6", + "/triangledownsld": "\u25BC", + "/triangleleftequal": "\u22B4", + "/triangleleftsld": "\u25C0", + "/trianglerightequal": "\u22B5", + "/trianglerightsld": "\u25B6", + "/trianglesolid": "\u25B2", + "/uprise": "\u22CF", + # diff : "/Digamma": "\u1D7C", + "/Finv": "\u2132", + "/Gmir": "\u2141", + "/Omegainv": "\u2127", + "/approxorequal": "\u224A", + "/archleftdown": "\u21B6", + "/archrightdown": "\u21B7", + "/beth": "\u2136", + "/daleth": "\u2138", + "/dividemultiply": "\u22C7", + "/downslope": "\u29F9", + "/equalorsimilar": "\u2242", + "/follownotdbleqv": "\u2ABA", + "/follownotslnteql": "\u2AB6", + "/followornoteqvlnt": "\u22E9", + "/greaternotdblequal": "\u2A8A", + "/greaternotequal": "\u2A88", + "/greaterornotdbleql": "\u2269", + "/greaterornotequal": "\u2269", + "/integerdivide": "\u2216", + "/lessnotdblequal": "\u2A89", + "/lessnotequal": "\u2A87", + "/lessornotdbleql": "\u2268", + "/lessornotequal": "\u2268", + "/multicloseleft": "\u22C9", + "/multicloseright": "\u22CA", + "/notapproxequal": "\u2247", + "/notarrowboth": "\u21AE", + "/notarrowleft": "\u219A", + "/notarrowright": "\u219B", + "/notbar": "\u2224", + "/notdblarrowboth": "\u21CE", + "/notdblarrowleft": "\u21CD", + "/notdblarrowright": "\u21CF", + "/notfollows": "\u2281", + "/notfollowsoreql": "\u2AB0", + "/notforces": "\u22AE", + "/notforcesextra": "\u22AF", + "/notgreaterdblequal": "\u2267", + "/notgreaterequal": "\u2271", + "/notgreaterorslnteql": "\u2A7E", + "/notlessdblequal": "\u2266", + "/notlessequal": "\u2270", + "/notlessorslnteql": "\u2A7D", + "/notprecedesoreql": "\u2AAF", + "/notsatisfies": "\u22AD", + "/notsimilar": "\u2241", + "/notsubseteql": "\u2288", + "/notsubsetordbleql": "\u2AC5", + "/notsubsetoreql": "\u228A", + "/notsuperseteql": "\u2289", + "/notsupersetordbleql": "\u2AC6", + "/notsupersetoreql": "\u228B", + "/nottriangeqlleft": "\u22EC", + "/nottriangeqlright": "\u22ED", + "/nottriangleleft": "\u22EA", + "/nottriangleright": "\u22EB", + "/notturnstile": "\u22AC", + "/planckover2pi": "\u210F", + "/planckover2pi1": "\u210F", + "/precedenotdbleqv": "\u2AB9", + "/precedenotslnteql": "\u2AB5", + "/precedeornoteqvlnt": "\u22E8", + "/subsetnoteql": "\u228A", + "/subsetornotdbleql": "\u2ACB", + "/supersetnoteql": "\u228B", + "/supersetornotdbleql": "\u2ACC", + "/upslope": "\u29F8", +} + + +def _complete() -> None: + global adobe_glyphs + for i in range(256): + adobe_glyphs[f"/a{i}"] = chr(i) + adobe_glyphs["/.notdef"] = "□" + + +_complete() diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/pdfdoc.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/pdfdoc.py new file mode 100644 index 00000000..306357a5 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/pdfdoc.py @@ -0,0 +1,264 @@ +# PDFDocEncoding Character Set: Table D.2 of PDF Reference 1.7 +# C.1 Predefined encodings sorted by character name of another PDF reference +# Some indices have '\u0000' although they should have something else: +# 22: should be '\u0017' +_pdfdoc_encoding = [ + "\u0000", + "\u0001", + "\u0002", + "\u0003", + "\u0004", + "\u0005", + "\u0006", + "\u0007", # 0 - 7 + "\u0008", + "\u0009", + "\u000a", + "\u000b", + "\u000c", + "\u000d", + "\u000e", + "\u000f", # 8 - 15 + "\u0010", + "\u0011", + "\u0012", + "\u0013", + "\u0014", + "\u0015", + "\u0000", + "\u0017", # 16 - 23 + "\u02d8", + "\u02c7", + "\u02c6", + "\u02d9", + "\u02dd", + "\u02db", + "\u02da", + "\u02dc", # 24 - 31 + "\u0020", + "\u0021", + "\u0022", + "\u0023", + "\u0024", + "\u0025", + "\u0026", + "\u0027", # 32 - 39 + "\u0028", + "\u0029", + "\u002a", + "\u002b", + "\u002c", + "\u002d", + "\u002e", + "\u002f", # 40 - 47 + "\u0030", + "\u0031", + "\u0032", + "\u0033", + "\u0034", + "\u0035", + "\u0036", + "\u0037", # 48 - 55 + "\u0038", + "\u0039", + "\u003a", + "\u003b", + "\u003c", + "\u003d", + "\u003e", + "\u003f", # 56 - 63 + "\u0040", + "\u0041", + "\u0042", + "\u0043", + "\u0044", + "\u0045", + "\u0046", + "\u0047", # 64 - 71 + "\u0048", + "\u0049", + "\u004a", + "\u004b", + "\u004c", + "\u004d", + "\u004e", + "\u004f", # 72 - 79 + "\u0050", + "\u0051", + "\u0052", + "\u0053", + "\u0054", + "\u0055", + "\u0056", + "\u0057", # 80 - 87 + "\u0058", + "\u0059", + "\u005a", + "\u005b", + "\u005c", + "\u005d", + "\u005e", + "\u005f", # 88 - 95 + "\u0060", + "\u0061", + "\u0062", + "\u0063", + "\u0064", + "\u0065", + "\u0066", + "\u0067", # 96 - 103 + "\u0068", + "\u0069", + "\u006a", + "\u006b", + "\u006c", + "\u006d", + "\u006e", + "\u006f", # 104 - 111 + "\u0070", + "\u0071", + "\u0072", + "\u0073", + "\u0074", + "\u0075", + "\u0076", + "\u0077", # 112 - 119 + "\u0078", + "\u0079", + "\u007a", + "\u007b", + "\u007c", + "\u007d", + "\u007e", + "\u0000", # 120 - 127 + "\u2022", + "\u2020", + "\u2021", + "\u2026", + "\u2014", + "\u2013", + "\u0192", + "\u2044", # 128 - 135 + "\u2039", + "\u203a", + "\u2212", + "\u2030", + "\u201e", + "\u201c", + "\u201d", + "\u2018", # 136 - 143 + "\u2019", + "\u201a", + "\u2122", + "\ufb01", + "\ufb02", + "\u0141", + "\u0152", + "\u0160", # 144 - 151 + "\u0178", + "\u017d", + "\u0131", + "\u0142", + "\u0153", + "\u0161", + "\u017e", + "\u0000", # 152 - 159 + "\u20ac", + "\u00a1", + "\u00a2", + "\u00a3", + "\u00a4", + "\u00a5", + "\u00a6", + "\u00a7", # 160 - 167 + "\u00a8", + "\u00a9", + "\u00aa", + "\u00ab", + "\u00ac", + "\u0000", + "\u00ae", + "\u00af", # 168 - 175 + "\u00b0", + "\u00b1", + "\u00b2", + "\u00b3", + "\u00b4", + "\u00b5", + "\u00b6", + "\u00b7", # 176 - 183 + "\u00b8", + "\u00b9", + "\u00ba", + "\u00bb", + "\u00bc", + "\u00bd", + "\u00be", + "\u00bf", # 184 - 191 + "\u00c0", + "\u00c1", + "\u00c2", + "\u00c3", + "\u00c4", + "\u00c5", + "\u00c6", + "\u00c7", # 192 - 199 + "\u00c8", + "\u00c9", + "\u00ca", + "\u00cb", + "\u00cc", + "\u00cd", + "\u00ce", + "\u00cf", # 200 - 207 + "\u00d0", + "\u00d1", + "\u00d2", + "\u00d3", + "\u00d4", + "\u00d5", + "\u00d6", + "\u00d7", # 208 - 215 + "\u00d8", + "\u00d9", + "\u00da", + "\u00db", + "\u00dc", + "\u00dd", + "\u00de", + "\u00df", # 216 - 223 + "\u00e0", + "\u00e1", + "\u00e2", + "\u00e3", + "\u00e4", + "\u00e5", + "\u00e6", + "\u00e7", # 224 - 231 + "\u00e8", + "\u00e9", + "\u00ea", + "\u00eb", + "\u00ec", + "\u00ed", + "\u00ee", + "\u00ef", # 232 - 239 + "\u00f0", + "\u00f1", + "\u00f2", + "\u00f3", + "\u00f4", + "\u00f5", + "\u00f6", + "\u00f7", # 240 - 247 + "\u00f8", + "\u00f9", + "\u00fa", + "\u00fb", + "\u00fc", + "\u00fd", + "\u00fe", + "\u00ff", # 248 - 255 +] + +assert len(_pdfdoc_encoding) == 256 diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/std.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/std.py new file mode 100644 index 00000000..a6057ff3 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/std.py @@ -0,0 +1,258 @@ +_std_encoding = [ + "\x00", + "\x01", + "\x02", + "\x03", + "\x04", + "\x05", + "\x06", + "\x07", + "\x08", + "\t", + "\n", + "\x0b", + "\x0c", + "\r", + "\x0e", + "\x0f", + "\x10", + "\x11", + "\x12", + "\x13", + "\x14", + "\x15", + "\x16", + "\x17", + "\x18", + "\x19", + "\x1a", + "\x1b", + "\x1c", + "\x1d", + "\x1e", + "\x1f", + " ", + "!", + '"', + "#", + "$", + "%", + "&", + "’", + "(", + ")", + "*", + "+", + ",", + "-", + ".", + "/", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "[", + "\\", + "]", + "^", + "_", + "‘", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "{", + "|", + "}", + "~", + "\x7f", + "\x80", + "\x81", + "\x82", + "\x83", + "\x84", + "\x85", + "\x86", + "\x87", + "\x88", + "\x89", + "\x8a", + "\x8b", + "\x8c", + "\x8d", + "\x8e", + "\x8f", + "\x90", + "\x91", + "\x92", + "\x93", + "\x94", + "\x95", + "\x96", + "\x97", + "\x98", + "\x99", + "\x9a", + "\x9b", + "\x9c", + "\x9d", + "\x9e", + "\x9f", + "\xa0", + "¡", + "¢", + "£", + "⁄", + "¥", + "ƒ", + "§", + "¤", + "'", + "“", + "«", + "‹", + "›", + "fi", + "fl", + "°", + "–", + "†", + "‡", + "·", + "µ", + "¶", + "•", + "‚", + "„", + "”", + "»", + "…", + "‰", + "¾", + "¿", + "À", + "`", + "´", + "ˆ", + "˜", + "¯", + "˘", + "˙", + "¨", + "É", + "˚", + "¸", + "Ì", + "˝", + "˛", + "ˇ", + "—", + "Ñ", + "Ò", + "Ó", + "Ô", + "Õ", + "Ö", + "×", + "Ø", + "Ù", + "Ú", + "Û", + "Ü", + "Ý", + "Þ", + "ß", + "à", + "Æ", + "â", + "ª", + "ä", + "å", + "æ", + "ç", + "Ł", + "Ø", + "Œ", + "º", + "ì", + "í", + "î", + "ï", + "ð", + "æ", + "ò", + "ó", + "ô", + "ı", + "ö", + "÷", + "ł", + "ø", + "œ", + "ß", + "ü", + "ý", + "þ", + "ÿ", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/symbol.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/symbol.py new file mode 100644 index 00000000..4c0d680f --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/symbol.py @@ -0,0 +1,260 @@ +# manually generated from https://www.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/symbol.txt +_symbol_encoding = [ + "\u0000", + "\u0001", + "\u0002", + "\u0003", + "\u0004", + "\u0005", + "\u0006", + "\u0007", + "\u0008", + "\u0009", + "\u000A", + "\u000B", + "\u000C", + "\u000D", + "\u000E", + "\u000F", + "\u0010", + "\u0011", + "\u0012", + "\u0013", + "\u0014", + "\u0015", + "\u0016", + "\u0017", + "\u0018", + "\u0019", + "\u001A", + "\u001B", + "\u001C", + "\u001D", + "\u001E", + "\u001F", + "\u0020", + "\u0021", + "\u2200", + "\u0023", + "\u2203", + "\u0025", + "\u0026", + "\u220B", + "\u0028", + "\u0029", + "\u2217", + "\u002B", + "\u002C", + "\u2212", + "\u002E", + "\u002F", + "\u0030", + "\u0031", + "\u0032", + "\u0033", + "\u0034", + "\u0035", + "\u0036", + "\u0037", + "\u0038", + "\u0039", + "\u003A", + "\u003B", + "\u003C", + "\u003D", + "\u003E", + "\u003F", + "\u2245", + "\u0391", + "\u0392", + "\u03A7", + "\u0394", + "\u0395", + "\u03A6", + "\u0393", + "\u0397", + "\u0399", + "\u03D1", + "\u039A", + "\u039B", + "\u039C", + "\u039D", + "\u039F", + "\u03A0", + "\u0398", + "\u03A1", + "\u03A3", + "\u03A4", + "\u03A5", + "\u03C2", + "\u03A9", + "\u039E", + "\u03A8", + "\u0396", + "\u005B", + "\u2234", + "\u005D", + "\u22A5", + "\u005F", + "\uF8E5", + "\u03B1", + "\u03B2", + "\u03C7", + "\u03B4", + "\u03B5", + "\u03C6", + "\u03B3", + "\u03B7", + "\u03B9", + "\u03D5", + "\u03BA", + "\u03BB", + "\u00B5", + "\u03BD", + "\u03BF", + "\u03C0", + "\u03B8", + "\u03C1", + "\u03C3", + "\u03C4", + "\u03C5", + "\u03D6", + "\u03C9", + "\u03BE", + "\u03C8", + "\u03B6", + "\u007B", + "\u007C", + "\u007D", + "\u223C", + "\u007F", + "\u0080", + "\u0081", + "\u0082", + "\u0083", + "\u0084", + "\u0085", + "\u0086", + "\u0087", + "\u0088", + "\u0089", + "\u008A", + "\u008B", + "\u008C", + "\u008D", + "\u008E", + "\u008F", + "\u0090", + "\u0091", + "\u0092", + "\u0093", + "\u0094", + "\u0095", + "\u0096", + "\u0097", + "\u0098", + "\u0099", + "\u009A", + "\u009B", + "\u009C", + "\u009D", + "\u009E", + "\u009F", + "\u20AC", + "\u03D2", + "\u2032", + "\u2264", + "\u2044", + "\u221E", + "\u0192", + "\u2663", + "\u2666", + "\u2665", + "\u2660", + "\u2194", + "\u2190", + "\u2191", + "\u2192", + "\u2193", + "\u00B0", + "\u00B1", + "\u2033", + "\u2265", + "\u00D7", + "\u221D", + "\u2202", + "\u2022", + "\u00F7", + "\u2260", + "\u2261", + "\u2248", + "\u2026", + "\uF8E6", + "\uF8E7", + "\u21B5", + "\u2135", + "\u2111", + "\u211C", + "\u2118", + "\u2297", + "\u2295", + "\u2205", + "\u2229", + "\u222A", + "\u2283", + "\u2287", + "\u2284", + "\u2282", + "\u2286", + "\u2208", + "\u2209", + "\u2220", + "\u2207", + "\uF6DA", + "\uF6D9", + "\uF6DB", + "\u220F", + "\u221A", + "\u22C5", + "\u00AC", + "\u2227", + "\u2228", + "\u21D4", + "\u21D0", + "\u21D1", + "\u21D2", + "\u21D3", + "\u25CA", + "\u2329", + "\uF8E8", + "\uF8E9", + "\uF8EA", + "\u2211", + "\uF8EB", + "\uF8EC", + "\uF8ED", + "\uF8EE", + "\uF8EF", + "\uF8F0", + "\uF8F1", + "\uF8F2", + "\uF8F3", + "\uF8F4", + "\u00F0", + "\u232A", + "\u222B", + "\u2320", + "\uF8F5", + "\u2321", + "\uF8F6", + "\uF8F7", + "\uF8F8", + "\uF8F9", + "\uF8FA", + "\uF8FB", + "\uF8FC", + "\uF8FD", + "\uF8FE", + "\u00FF", +] +assert len(_symbol_encoding) == 256 diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/zapfding.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/zapfding.py new file mode 100644 index 00000000..9b6cdbcc --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_codecs/zapfding.py @@ -0,0 +1,261 @@ +# manually generated from https://www.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/zdingbat.txt + +_zapfding_encoding = [ + "\u0000", + "\u0001", + "\u0002", + "\u0003", + "\u0004", + "\u0005", + "\u0006", + "\u0007", + "\u0008", + "\u0009", + "\u000A", + "\u000B", + "\u000C", + "\u000D", + "\u000E", + "\u000F", + "\u0010", + "\u0011", + "\u0012", + "\u0013", + "\u0014", + "\u0015", + "\u0016", + "\u0017", + "\u0018", + "\u0019", + "\u001A", + "\u001B", + "\u001C", + "\u001D", + "\u001E", + "\u001F", + "\u0020", + "\u2701", + "\u2702", + "\u2703", + "\u2704", + "\u260E", + "\u2706", + "\u2707", + "\u2708", + "\u2709", + "\u261B", + "\u261E", + "\u270C", + "\u270D", + "\u270E", + "\u270F", + "\u2710", + "\u2711", + "\u2712", + "\u2713", + "\u2714", + "\u2715", + "\u2716", + "\u2717", + "\u2718", + "\u2719", + "\u271A", + "\u271B", + "\u271C", + "\u271D", + "\u271E", + "\u271F", + "\u2720", + "\u2721", + "\u2722", + "\u2723", + "\u2724", + "\u2725", + "\u2726", + "\u2727", + "\u2605", + "\u2729", + "\u272A", + "\u272B", + "\u272C", + "\u272D", + "\u272E", + "\u272F", + "\u2730", + "\u2731", + "\u2732", + "\u2733", + "\u2734", + "\u2735", + "\u2736", + "\u2737", + "\u2738", + "\u2739", + "\u273A", + "\u273B", + "\u273C", + "\u273D", + "\u273E", + "\u273F", + "\u2740", + "\u2741", + "\u2742", + "\u2743", + "\u2744", + "\u2745", + "\u2746", + "\u2747", + "\u2748", + "\u2749", + "\u274A", + "\u274B", + "\u25CF", + "\u274D", + "\u25A0", + "\u274F", + "\u2750", + "\u2751", + "\u2752", + "\u25B2", + "\u25BC", + "\u25C6", + "\u2756", + "\u25D7", + "\u2758", + "\u2759", + "\u275A", + "\u275B", + "\u275C", + "\u275D", + "\u275E", + "\u007F", + "\uF8D7", + "\uF8D8", + "\uF8D9", + "\uF8DA", + "\uF8DB", + "\uF8DC", + "\uF8DD", + "\uF8DE", + "\uF8DF", + "\uF8E0", + "\uF8E1", + "\uF8E2", + "\uF8E3", + "\uF8E4", + "\u008E", + "\u008F", + "\u0090", + "\u0091", + "\u0092", + "\u0093", + "\u0094", + "\u0095", + "\u0096", + "\u0097", + "\u0098", + "\u0099", + "\u009A", + "\u009B", + "\u009C", + "\u009D", + "\u009E", + "\u009F", + "\u00A0", + "\u2761", + "\u2762", + "\u2763", + "\u2764", + "\u2765", + "\u2766", + "\u2767", + "\u2663", + "\u2666", + "\u2665", + "\u2660", + "\u2460", + "\u2461", + "\u2462", + "\u2463", + "\u2464", + "\u2465", + "\u2466", + "\u2467", + "\u2468", + "\u2469", + "\u2776", + "\u2777", + "\u2778", + "\u2779", + "\u277A", + "\u277B", + "\u277C", + "\u277D", + "\u277E", + "\u277F", + "\u2780", + "\u2781", + "\u2782", + "\u2783", + "\u2784", + "\u2785", + "\u2786", + "\u2787", + "\u2788", + "\u2789", + "\u278A", + "\u278B", + "\u278C", + "\u278D", + "\u278E", + "\u278F", + "\u2790", + "\u2791", + "\u2792", + "\u2793", + "\u2794", + "\u2192", + "\u2194", + "\u2195", + "\u2798", + "\u2799", + "\u279A", + "\u279B", + "\u279C", + "\u279D", + "\u279E", + "\u279F", + "\u27A0", + "\u27A1", + "\u27A2", + "\u27A3", + "\u27A4", + "\u27A5", + "\u27A6", + "\u27A7", + "\u27A8", + "\u27A9", + "\u27AA", + "\u27AB", + "\u27AC", + "\u27AD", + "\u27AE", + "\u27AF", + "\u00F0", + "\u27B1", + "\u27B2", + "\u27B3", + "\u27B4", + "\u27B5", + "\u27B6", + "\u27B7", + "\u27B8", + "\u27B9", + "\u27BA", + "\u27BB", + "\u27BC", + "\u27BD", + "\u27BE", + "\u00FF", +] +assert len(_zapfding_encoding) == 256 diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__init__.py new file mode 100644 index 00000000..49d41128 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__init__.py @@ -0,0 +1,86 @@ +# Copyright (c) 2023, exiledkingcc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from pypdf._crypt_providers._base import CryptBase, CryptIdentity + +try: + from pypdf._crypt_providers._cryptography import ( + CryptAES, + CryptRC4, + aes_cbc_decrypt, + aes_cbc_encrypt, + aes_ecb_decrypt, + aes_ecb_encrypt, + crypt_provider, + rc4_decrypt, + rc4_encrypt, + ) + from pypdf._utils import Version + + if Version(crypt_provider[1]) <= Version("3.0"): + # This is due to the backend parameter being required back then: + # https://cryptography.io/en/latest/changelog/#v3-1 + raise ImportError("cryptography<=3.0 is not supported") # pragma: no cover +except ImportError: + try: + from pypdf._crypt_providers._pycryptodome import ( # type: ignore + CryptAES, + CryptRC4, + aes_cbc_decrypt, + aes_cbc_encrypt, + aes_ecb_decrypt, + aes_ecb_encrypt, + crypt_provider, + rc4_decrypt, + rc4_encrypt, + ) + except ImportError: + from pypdf._crypt_providers._fallback import ( # type: ignore + CryptAES, + CryptRC4, + aes_cbc_decrypt, + aes_cbc_encrypt, + aes_ecb_decrypt, + aes_ecb_encrypt, + crypt_provider, + rc4_decrypt, + rc4_encrypt, + ) + +__all__ = [ + "crypt_provider", + "CryptBase", + "CryptIdentity", + "CryptRC4", + "CryptAES", + "rc4_encrypt", + "rc4_decrypt", + "aes_ecb_encrypt", + "aes_ecb_decrypt", + "aes_cbc_encrypt", + "aes_cbc_decrypt", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..e397b246 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_base.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_base.cpython-38.pyc new file mode 100644 index 00000000..149f9868 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_base.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_cryptography.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_cryptography.cpython-38.pyc new file mode 100644 index 00000000..6e01c2a9 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_cryptography.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_fallback.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_fallback.cpython-38.pyc new file mode 100644 index 00000000..5e1c7ea1 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_fallback.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_pycryptodome.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_pycryptodome.cpython-38.pyc new file mode 100644 index 00000000..645bd63a Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/__pycache__/_pycryptodome.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_base.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_base.py new file mode 100644 index 00000000..894025f3 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_base.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023, exiledkingcc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +class CryptBase: + def encrypt(self, data: bytes) -> bytes: # pragma: no cover + return data + + def decrypt(self, data: bytes) -> bytes: # pragma: no cover + return data + + +class CryptIdentity(CryptBase): + pass diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_cryptography.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_cryptography.py new file mode 100644 index 00000000..f5537612 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_cryptography.py @@ -0,0 +1,118 @@ +# Copyright (c) 2023, exiledkingcc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import secrets + +from cryptography import __version__ +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives.ciphers.algorithms import AES + +try: + # 43.0.0 - https://cryptography.io/en/latest/changelog/#v43-0-0 + from cryptography.hazmat.decrepit.ciphers.algorithms import ARC4 +except ImportError: + from cryptography.hazmat.primitives.ciphers.algorithms import ARC4 +from cryptography.hazmat.primitives.ciphers.base import Cipher +from cryptography.hazmat.primitives.ciphers.modes import CBC, ECB + +from pypdf._crypt_providers._base import CryptBase + +crypt_provider = ("cryptography", __version__) + + +class CryptRC4(CryptBase): + def __init__(self, key: bytes) -> None: + self.cipher = Cipher(ARC4(key), mode=None) + + def encrypt(self, data: bytes) -> bytes: + encryptor = self.cipher.encryptor() + return encryptor.update(data) + encryptor.finalize() + + def decrypt(self, data: bytes) -> bytes: + decryptor = self.cipher.decryptor() + return decryptor.update(data) + decryptor.finalize() + + +class CryptAES(CryptBase): + def __init__(self, key: bytes) -> None: + self.alg = AES(key) + + def encrypt(self, data: bytes) -> bytes: + iv = secrets.token_bytes(16) + pad = padding.PKCS7(128).padder() + data = pad.update(data) + pad.finalize() + + cipher = Cipher(self.alg, CBC(iv)) + encryptor = cipher.encryptor() + return iv + encryptor.update(data) + encryptor.finalize() + + def decrypt(self, data: bytes) -> bytes: + iv = data[:16] + data = data[16:] + # for empty encrypted data + if not data: + return data + + # just for robustness, it does not happen under normal circumstances + if len(data) % 16 != 0: + pad = padding.PKCS7(128).padder() + data = pad.update(data) + pad.finalize() + + cipher = Cipher(self.alg, CBC(iv)) + decryptor = cipher.decryptor() + d = decryptor.update(data) + decryptor.finalize() + return d[: -d[-1]] + + +def rc4_encrypt(key: bytes, data: bytes) -> bytes: + encryptor = Cipher(ARC4(key), mode=None).encryptor() + return encryptor.update(data) + encryptor.finalize() + + +def rc4_decrypt(key: bytes, data: bytes) -> bytes: + decryptor = Cipher(ARC4(key), mode=None).decryptor() + return decryptor.update(data) + decryptor.finalize() + + +def aes_ecb_encrypt(key: bytes, data: bytes) -> bytes: + encryptor = Cipher(AES(key), mode=ECB()).encryptor() + return encryptor.update(data) + encryptor.finalize() + + +def aes_ecb_decrypt(key: bytes, data: bytes) -> bytes: + decryptor = Cipher(AES(key), mode=ECB()).decryptor() + return decryptor.update(data) + decryptor.finalize() + + +def aes_cbc_encrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + encryptor = Cipher(AES(key), mode=CBC(iv)).encryptor() + return encryptor.update(data) + encryptor.finalize() + + +def aes_cbc_decrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + decryptor = Cipher(AES(key), mode=CBC(iv)).decryptor() + return decryptor.update(data) + decryptor.finalize() diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_fallback.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_fallback.py new file mode 100644 index 00000000..631fec19 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_fallback.py @@ -0,0 +1,93 @@ +# Copyright (c) 2023, exiledkingcc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from pypdf._crypt_providers._base import CryptBase +from pypdf.errors import DependencyError + +_DEPENDENCY_ERROR_STR = "cryptography>=3.1 is required for AES algorithm" + + +crypt_provider = ("local_crypt_fallback", "0.0.0") + + +class CryptRC4(CryptBase): + def __init__(self, key: bytes) -> None: + self.s = bytearray(range(256)) + j = 0 + for i in range(256): + j = (j + self.s[i] + key[i % len(key)]) % 256 + self.s[i], self.s[j] = self.s[j], self.s[i] + + def encrypt(self, data: bytes) -> bytes: + s = bytearray(self.s) + out = [0 for _ in range(len(data))] + i, j = 0, 0 + for k in range(len(data)): + i = (i + 1) % 256 + j = (j + s[i]) % 256 + s[i], s[j] = s[j], s[i] + x = s[(s[i] + s[j]) % 256] + out[k] = data[k] ^ x + return bytes(bytearray(out)) + + def decrypt(self, data: bytes) -> bytes: + return self.encrypt(data) + + +class CryptAES(CryptBase): + def __init__(self, key: bytes) -> None: + pass + + def encrypt(self, data: bytes) -> bytes: + raise DependencyError(_DEPENDENCY_ERROR_STR) + + def decrypt(self, data: bytes) -> bytes: + raise DependencyError(_DEPENDENCY_ERROR_STR) + + +def rc4_encrypt(key: bytes, data: bytes) -> bytes: + return CryptRC4(key).encrypt(data) + + +def rc4_decrypt(key: bytes, data: bytes) -> bytes: + return CryptRC4(key).decrypt(data) + + +def aes_ecb_encrypt(key: bytes, data: bytes) -> bytes: + raise DependencyError(_DEPENDENCY_ERROR_STR) + + +def aes_ecb_decrypt(key: bytes, data: bytes) -> bytes: + raise DependencyError(_DEPENDENCY_ERROR_STR) + + +def aes_cbc_encrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + raise DependencyError(_DEPENDENCY_ERROR_STR) + + +def aes_cbc_decrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + raise DependencyError(_DEPENDENCY_ERROR_STR) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_pycryptodome.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_pycryptodome.py new file mode 100644 index 00000000..30a13e18 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_crypt_providers/_pycryptodome.py @@ -0,0 +1,97 @@ +# Copyright (c) 2023, exiledkingcc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import secrets + +from Crypto import __version__ +from Crypto.Cipher import AES, ARC4 +from Crypto.Util.Padding import pad + +from pypdf._crypt_providers._base import CryptBase + +crypt_provider = ("pycryptodome", __version__) + + +class CryptRC4(CryptBase): + def __init__(self, key: bytes) -> None: + self.key = key + + def encrypt(self, data: bytes) -> bytes: + return ARC4.ARC4Cipher(self.key).encrypt(data) + + def decrypt(self, data: bytes) -> bytes: + return ARC4.ARC4Cipher(self.key).decrypt(data) + + +class CryptAES(CryptBase): + def __init__(self, key: bytes) -> None: + self.key = key + + def encrypt(self, data: bytes) -> bytes: + iv = secrets.token_bytes(16) + data = pad(data, 16) + aes = AES.new(self.key, AES.MODE_CBC, iv) + return iv + aes.encrypt(data) + + def decrypt(self, data: bytes) -> bytes: + iv = data[:16] + data = data[16:] + # for empty encrypted data + if not data: + return data + + # just for robustness, it does not happen under normal circumstances + if len(data) % 16 != 0: + data = pad(data, 16) + + aes = AES.new(self.key, AES.MODE_CBC, iv) + d = aes.decrypt(data) + return d[: -d[-1]] + + +def rc4_encrypt(key: bytes, data: bytes) -> bytes: + return ARC4.ARC4Cipher(key).encrypt(data) + + +def rc4_decrypt(key: bytes, data: bytes) -> bytes: + return ARC4.ARC4Cipher(key).decrypt(data) + + +def aes_ecb_encrypt(key: bytes, data: bytes) -> bytes: + return AES.new(key, AES.MODE_ECB).encrypt(data) + + +def aes_ecb_decrypt(key: bytes, data: bytes) -> bytes: + return AES.new(key, AES.MODE_ECB).decrypt(data) + + +def aes_cbc_encrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + return AES.new(key, AES.MODE_CBC, iv).encrypt(data) + + +def aes_cbc_decrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + return AES.new(key, AES.MODE_CBC, iv).decrypt(data) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_doc_common.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_doc_common.py new file mode 100644 index 00000000..55c6aad6 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_doc_common.py @@ -0,0 +1,1420 @@ +# Copyright (c) 2006, Mathieu Fenniak +# Copyright (c) 2007, Ashish Kulkarni +# Copyright (c) 2024, Pubpub-ZZ +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import struct +import zlib +from abc import abstractmethod +from datetime import datetime +from typing import ( + Any, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Tuple, + Union, + cast, +) + +from ._encryption import Encryption +from ._page import PageObject, _VirtualList +from ._page_labels import index2label as page_index2page_label +from ._utils import ( + deprecate_with_replacement, + logger_warning, + parse_iso8824_date, +) +from .constants import CatalogAttributes as CA +from .constants import CatalogDictionary as CD +from .constants import ( + CheckboxRadioButtonAttributes, + GoToActionArguments, + UserAccessPermissions, +) +from .constants import Core as CO +from .constants import DocumentInformationAttributes as DI +from .constants import FieldDictionaryAttributes as FA +from .constants import PageAttributes as PG +from .constants import PagesAttributes as PA +from .errors import PdfReadError, PyPdfError +from .generic import ( + ArrayObject, + BooleanObject, + ByteStringObject, + Destination, + DictionaryObject, + EncodedStreamObject, + Field, + Fit, + FloatObject, + IndirectObject, + NameObject, + NullObject, + NumberObject, + PdfObject, + TextStringObject, + TreeObject, + ViewerPreferences, + create_string_object, + is_null_or_none, +) +from .types import OutlineType, PagemodeType +from .xmp import XmpInformation + + +def convert_to_int(d: bytes, size: int) -> Union[int, Tuple[Any, ...]]: + if size > 8: + raise PdfReadError("invalid size in convert_to_int") + d = b"\x00\x00\x00\x00\x00\x00\x00\x00" + d + d = d[-8:] + return struct.unpack(">q", d)[0] + + +class DocumentInformation(DictionaryObject): + """ + A class representing the basic document metadata provided in a PDF File. + This class is accessible through + :py:class:`PdfReader.metadata`. + + All text properties of the document metadata have + *two* properties, e.g. author and author_raw. The non-raw property will + always return a ``TextStringObject``, making it ideal for a case where the + metadata is being displayed. The raw property can sometimes return a + ``ByteStringObject``, if pypdf was unable to decode the string's text + encoding; this requires additional safety in the caller and therefore is not + as commonly accessed. + """ + + def __init__(self) -> None: + DictionaryObject.__init__(self) + + def _get_text(self, key: str) -> Optional[str]: + retval = self.get(key, None) + if isinstance(retval, TextStringObject): + return retval + return None + + @property + def title(self) -> Optional[str]: + """ + Read-only property accessing the document's title. + + Returns a ``TextStringObject`` or ``None`` if the title is not + specified. + """ + return ( + self._get_text(DI.TITLE) or self.get(DI.TITLE).get_object() # type: ignore + if self.get(DI.TITLE) + else None + ) + + @property + def title_raw(self) -> Optional[str]: + """The "raw" version of title; can return a ``ByteStringObject``.""" + return self.get(DI.TITLE) + + @property + def author(self) -> Optional[str]: + """ + Read-only property accessing the document's author. + + Returns a ``TextStringObject`` or ``None`` if the author is not + specified. + """ + return self._get_text(DI.AUTHOR) + + @property + def author_raw(self) -> Optional[str]: + """The "raw" version of author; can return a ``ByteStringObject``.""" + return self.get(DI.AUTHOR) + + @property + def subject(self) -> Optional[str]: + """ + Read-only property accessing the document's subject. + + Returns a ``TextStringObject`` or ``None`` if the subject is not + specified. + """ + return self._get_text(DI.SUBJECT) + + @property + def subject_raw(self) -> Optional[str]: + """The "raw" version of subject; can return a ``ByteStringObject``.""" + return self.get(DI.SUBJECT) + + @property + def creator(self) -> Optional[str]: + """ + Read-only property accessing the document's creator. + + If the document was converted to PDF from another format, this is the + name of the application (e.g. OpenOffice) that created the original + document from which it was converted. Returns a ``TextStringObject`` or + ``None`` if the creator is not specified. + """ + return self._get_text(DI.CREATOR) + + @property + def creator_raw(self) -> Optional[str]: + """The "raw" version of creator; can return a ``ByteStringObject``.""" + return self.get(DI.CREATOR) + + @property + def producer(self) -> Optional[str]: + """ + Read-only property accessing the document's producer. + + If the document was converted to PDF from another format, this is the + name of the application (for example, macOS Quartz) that converted it to + PDF. Returns a ``TextStringObject`` or ``None`` if the producer is not + specified. + """ + return self._get_text(DI.PRODUCER) + + @property + def producer_raw(self) -> Optional[str]: + """The "raw" version of producer; can return a ``ByteStringObject``.""" + return self.get(DI.PRODUCER) + + @property + def creation_date(self) -> Optional[datetime]: + """Read-only property accessing the document's creation date.""" + return parse_iso8824_date(self._get_text(DI.CREATION_DATE)) + + @property + def creation_date_raw(self) -> Optional[str]: + """ + The "raw" version of creation date; can return a ``ByteStringObject``. + + Typically in the format ``D:YYYYMMDDhhmmss[+Z-]hh'mm`` where the suffix + is the offset from UTC. + """ + return self.get(DI.CREATION_DATE) + + @property + def modification_date(self) -> Optional[datetime]: + """ + Read-only property accessing the document's modification date. + + The date and time the document was most recently modified. + """ + return parse_iso8824_date(self._get_text(DI.MOD_DATE)) + + @property + def modification_date_raw(self) -> Optional[str]: + """ + The "raw" version of modification date; can return a + ``ByteStringObject``. + + Typically in the format ``D:YYYYMMDDhhmmss[+Z-]hh'mm`` where the suffix + is the offset from UTC. + """ + return self.get(DI.MOD_DATE) + + +class PdfDocCommon: + """ + Common functions from PdfWriter and PdfReader objects. + + This root class is strongly abstracted. + """ + + strict: bool = False # default + + _encryption: Optional[Encryption] = None + + _readonly: bool = False + + @property + @abstractmethod + def root_object(self) -> DictionaryObject: + ... # pragma: no cover + + @property + @abstractmethod + def pdf_header(self) -> str: + ... # pragma: no cover + + @abstractmethod + def get_object( + self, indirect_reference: Union[int, IndirectObject] + ) -> Optional[PdfObject]: + ... # pragma: no cover + + @abstractmethod + def _replace_object(self, indirect: IndirectObject, obj: PdfObject) -> PdfObject: + ... # pragma: no cover + + @property + @abstractmethod + def _info(self) -> Optional[DictionaryObject]: + ... # pragma: no cover + + @property + def metadata(self) -> Optional[DocumentInformation]: + """ + Retrieve the PDF file's document information dictionary, if it exists. + + Note that some PDF files use metadata streams instead of document + information dictionaries, and these metadata streams will not be + accessed by this function. + """ + retval = DocumentInformation() + if self._info is None: + return None + retval.update(self._info) + return retval + + @property + def xmp_metadata(self) -> Optional[XmpInformation]: + ... # pragma: no cover + + @abstractmethod + def _repr_mimebundle_( + self, + include: Union[None, Iterable[str]] = None, + exclude: Union[None, Iterable[str]] = None, + ) -> Dict[str, Any]: + """ + Integration into Jupyter Notebooks. + + This method returns a dictionary that maps a mime-type to its + representation. + + See https://ipython.readthedocs.io/en/stable/config/integrating.html + """ + ... # pragma: no cover + + @property + def viewer_preferences(self) -> Optional[ViewerPreferences]: + """Returns the existing ViewerPreferences as an overloaded dictionary.""" + o = self.root_object.get(CD.VIEWER_PREFERENCES, None) + if o is None: + return None + o = o.get_object() + if not isinstance(o, ViewerPreferences): + o = ViewerPreferences(o) + if hasattr(o, "indirect_reference"): + self._replace_object(o.indirect_reference, o) + else: + self.root_object[NameObject(CD.VIEWER_PREFERENCES)] = o + return o + + flattened_pages: Optional[List[PageObject]] = None + + def get_num_pages(self) -> int: + """ + Calculate the number of pages in this PDF file. + + Returns: + The number of pages of the parsed PDF file. + + Raises: + PdfReadError: if file is encrypted and restrictions prevent + this action. + """ + # Flattened pages will not work on an encrypted PDF; + # the PDF file's page count is used in this case. Otherwise, + # the original method (flattened page count) is used. + if self.is_encrypted: + return self.root_object["/Pages"]["/Count"] # type: ignore + else: + if self.flattened_pages is None: + self._flatten(self._readonly) + assert self.flattened_pages is not None + return len(self.flattened_pages) + + def get_page(self, page_number: int) -> PageObject: + """ + Retrieve a page by number from this PDF file. + Most of the time ``.pages[page_number]`` is preferred. + + Args: + page_number: The page number to retrieve + (pages begin at zero) + + Returns: + A :class:`PageObject` instance. + """ + if self.flattened_pages is None: + self._flatten(self._readonly) + assert self.flattened_pages is not None, "hint for mypy" + return self.flattened_pages[page_number] + + def _get_page_in_node( + self, + page_number: int, + ) -> Tuple[DictionaryObject, int]: + """ + Retrieve the node and position within the /Kids containing the page. + If page_number is greater than the number of pages, it returns the top node, -1. + """ + top = cast(DictionaryObject, self.root_object["/Pages"]) + + def recursive_call( + node: DictionaryObject, mi: int + ) -> Tuple[Optional[PdfObject], int]: + ma = cast(int, node.get("/Count", 1)) # default 1 for /Page types + if node["/Type"] == "/Page": + if page_number == mi: + return node, -1 + # else + return None, mi + 1 + if (page_number - mi) >= ma: # not in nodes below + if node == top: + return top, -1 + # else + return None, mi + ma + for idx, kid in enumerate(cast(ArrayObject, node["/Kids"])): + kid = cast(DictionaryObject, kid.get_object()) + n, i = recursive_call(kid, mi) + if n is not None: # page has just been found ... + if i < 0: # ... just below! + return node, idx + # else: # ... at lower levels + return n, i + mi = i + raise PyPdfError("Unexpectedly cannot find the node.") + + node, idx = recursive_call(top, 0) + assert isinstance(node, DictionaryObject), "mypy" + return node, idx + + @property + def named_destinations(self) -> Dict[str, Any]: + """ + A read-only dictionary which maps names to + :class:`Destinations` + """ + return self._get_named_destinations() + + def get_named_dest_root(self) -> ArrayObject: + named_dest = ArrayObject() + if CA.NAMES in self.root_object and isinstance( + self.root_object[CA.NAMES], DictionaryObject + ): + names = cast(DictionaryObject, self.root_object[CA.NAMES]) + names_ref = names.indirect_reference + if CA.DESTS in names and isinstance(names[CA.DESTS], DictionaryObject): + # 3.6.3 Name Dictionary (PDF spec 1.7) + dests = cast(DictionaryObject, names[CA.DESTS]) + dests_ref = dests.indirect_reference + if CA.NAMES in dests: + # §7.9.6, entries in a name tree node dictionary + named_dest = cast(ArrayObject, dests[CA.NAMES]) + else: + named_dest = ArrayObject() + dests[NameObject(CA.NAMES)] = named_dest + elif hasattr(self, "_add_object"): + dests = DictionaryObject() + dests_ref = self._add_object(dests) + names[NameObject(CA.DESTS)] = dests_ref + dests[NameObject(CA.NAMES)] = named_dest + + elif hasattr(self, "_add_object"): + names = DictionaryObject() + names_ref = self._add_object(names) + self.root_object[NameObject(CA.NAMES)] = names_ref + dests = DictionaryObject() + dests_ref = self._add_object(dests) + names[NameObject(CA.DESTS)] = dests_ref + dests[NameObject(CA.NAMES)] = named_dest + + return named_dest + + ## common + def _get_named_destinations( + self, + tree: Union[TreeObject, None] = None, + retval: Optional[Any] = None, + ) -> Dict[str, Any]: + """ + Retrieve the named destinations present in the document. + + Args: + tree: + retval: + + Returns: + A dictionary which maps names to + :class:`Destinations`. + """ + if retval is None: + retval = {} + catalog = self.root_object + + # get the name tree + if CA.DESTS in catalog: + tree = cast(TreeObject, catalog[CA.DESTS]) + elif CA.NAMES in catalog: + names = cast(DictionaryObject, catalog[CA.NAMES]) + if CA.DESTS in names: + tree = cast(TreeObject, names[CA.DESTS]) + + if tree is None: + return retval + + if PA.KIDS in tree: + # recurse down the tree + for kid in cast(ArrayObject, tree[PA.KIDS]): + self._get_named_destinations(kid.get_object(), retval) + # §7.9.6, entries in a name tree node dictionary + elif CA.NAMES in tree: # /Kids and /Names are exclusives (§7.9.6) + names = cast(DictionaryObject, tree[CA.NAMES]) + i = 0 + while i < len(names): + key = cast(str, names[i].get_object()) + i += 1 + if not isinstance(key, str): + continue + try: + value = names[i].get_object() + except IndexError: + break + i += 1 + if isinstance(value, DictionaryObject): + if "/D" in value: + value = value["/D"] + else: + continue + dest = self._build_destination(key, value) # type: ignore + if dest is not None: + retval[key] = dest + else: # case where Dests is in root catalog (PDF 1.7 specs, §2 about PDF 1.1) + for k__, v__ in tree.items(): + val = v__.get_object() + if isinstance(val, DictionaryObject): + if "/D" in val: + val = val["/D"].get_object() + else: + continue + dest = self._build_destination(k__, val) + if dest is not None: + retval[k__] = dest + return retval + + # A select group of relevant field attributes. For the complete list. + # See §12.3.2 of the PDF 1.7 or PDF 2.0 specification. + + def get_fields( + self, + tree: Optional[TreeObject] = None, + retval: Optional[Dict[Any, Any]] = None, + fileobj: Optional[Any] = None, + stack: Optional[List[PdfObject]] = None, + ) -> Optional[Dict[str, Any]]: + """ + Extract field data if this PDF contains interactive form fields. + + The *tree*, *retval*, *stack* parameters are for recursive use. + + Args: + tree: Current object to parse. + retval: In-progress list of fields. + fileobj: A file object (usually a text file) to write + a report to on all interactive form fields found. + stack: List of already parsed objects. + + Returns: + A dictionary where each key is a field name, and each + value is a :class:`Field` object. By + default, the mapping name is used for keys. + ``None`` if form data could not be located. + """ + field_attributes = FA.attributes_dict() + field_attributes.update(CheckboxRadioButtonAttributes.attributes_dict()) + if retval is None: + retval = {} + catalog = self.root_object + stack = [] + # get the AcroForm tree + if CD.ACRO_FORM in catalog: + tree = cast(Optional[TreeObject], catalog[CD.ACRO_FORM]) + else: + return None + if tree is None: + return retval + assert stack is not None + if "/Fields" in tree: + fields = cast(ArrayObject, tree["/Fields"]) + for f in fields: + field = f.get_object() + self._build_field(field, retval, fileobj, field_attributes, stack) + elif any(attr in tree for attr in field_attributes): + # Tree is a field + self._build_field(tree, retval, fileobj, field_attributes, stack) + return retval + + def _get_qualified_field_name(self, parent: DictionaryObject) -> str: + if "/TM" in parent: + return cast(str, parent["/TM"]) + elif "/Parent" in parent: + return ( + self._get_qualified_field_name( + cast(DictionaryObject, parent["/Parent"]) + ) + + "." + + cast(str, parent.get("/T", "")) + ) + else: + return cast(str, parent.get("/T", "")) + + def _build_field( + self, + field: Union[TreeObject, DictionaryObject], + retval: Dict[Any, Any], + fileobj: Any, + field_attributes: Any, + stack: List[PdfObject], + ) -> None: + if all(attr not in field for attr in ("/T", "/TM")): + return + key = self._get_qualified_field_name(field) + if fileobj: + self._write_field(fileobj, field, field_attributes) + fileobj.write("\n") + retval[key] = Field(field) + obj = retval[key].indirect_reference.get_object() # to get the full object + if obj.get(FA.FT, "") == "/Ch": + retval[key][NameObject("/_States_")] = obj[NameObject(FA.Opt)] + if obj.get(FA.FT, "") == "/Btn" and "/AP" in obj: + # Checkbox + retval[key][NameObject("/_States_")] = ArrayObject( + list(obj["/AP"]["/N"].keys()) + ) + if "/Off" not in retval[key]["/_States_"]: + retval[key][NameObject("/_States_")].append(NameObject("/Off")) + elif obj.get(FA.FT, "") == "/Btn" and obj.get(FA.Ff, 0) & FA.FfBits.Radio != 0: + states: List[str] = [] + retval[key][NameObject("/_States_")] = ArrayObject(states) + for k in obj.get(FA.Kids, {}): + k = k.get_object() + for s in list(k["/AP"]["/N"].keys()): + if s not in states: + states.append(s) + retval[key][NameObject("/_States_")] = ArrayObject(states) + if ( + obj.get(FA.Ff, 0) & FA.FfBits.NoToggleToOff != 0 + and "/Off" in retval[key]["/_States_"] + ): + del retval[key]["/_States_"][retval[key]["/_States_"].index("/Off")] + # at last for order + self._check_kids(field, retval, fileobj, stack) + + def _check_kids( + self, + tree: Union[TreeObject, DictionaryObject], + retval: Any, + fileobj: Any, + stack: List[PdfObject], + ) -> None: + if tree in stack: + logger_warning( + f"{self._get_qualified_field_name(tree)} already parsed", __name__ + ) + return + stack.append(tree) + if PA.KIDS in tree: + # recurse down the tree + for kid in tree[PA.KIDS]: # type: ignore + kid = kid.get_object() + self.get_fields(kid, retval, fileobj, stack) + + def _write_field(self, fileobj: Any, field: Any, field_attributes: Any) -> None: + field_attributes_tuple = FA.attributes() + field_attributes_tuple = ( + field_attributes_tuple + CheckboxRadioButtonAttributes.attributes() + ) + + for attr in field_attributes_tuple: + if attr in ( + FA.Kids, + FA.AA, + ): + continue + attr_name = field_attributes[attr] + try: + if attr == FA.FT: + # Make the field type value more clear + types = { + "/Btn": "Button", + "/Tx": "Text", + "/Ch": "Choice", + "/Sig": "Signature", + } + if field[attr] in types: + fileobj.write(f"{attr_name}: {types[field[attr]]}\n") + elif attr == FA.Parent: + # Let's just write the name of the parent + try: + name = field[attr][FA.TM] + except KeyError: + name = field[attr][FA.T] + fileobj.write(f"{attr_name}: {name}\n") + else: + fileobj.write(f"{attr_name}: {field[attr]}\n") + except KeyError: + # Field attribute is N/A or unknown, so don't write anything + pass + + def get_form_text_fields(self, full_qualified_name: bool = False) -> Dict[str, Any]: + """ + Retrieve form fields from the document with textual data. + + Args: + full_qualified_name: to get full name + + Returns: + A dictionary. The key is the name of the form field, + the value is the content of the field. + + If the document contains multiple form fields with the same name, the + second and following will get the suffix .2, .3, ... + """ + + def indexed_key(k: str, fields: Dict[Any, Any]) -> str: + if k not in fields: + return k + else: + return ( + k + + "." + + str(sum([1 for kk in fields if kk.startswith(k + ".")]) + 2) + ) + + # Retrieve document form fields + formfields = self.get_fields() + if formfields is None: + return {} + ff = {} + for field, value in formfields.items(): + if value.get("/FT") == "/Tx": + if full_qualified_name: + ff[field] = value.get("/V") + else: + ff[indexed_key(cast(str, value["/T"]), ff)] = value.get("/V") + return ff + + def get_pages_showing_field( + self, field: Union[Field, PdfObject, IndirectObject] + ) -> List[PageObject]: + """ + Provides list of pages where the field is called. + + Args: + field: Field Object, PdfObject or IndirectObject referencing a Field + + Returns: + List of pages: + - Empty list: + The field has no widgets attached + (either hidden field or ancestor field). + - Single page list: + Page where the widget is present + (most common). + - Multi-page list: + Field with multiple kids widgets + (example: radio buttons, field repeated on multiple pages). + """ + + def _get_inherited(obj: DictionaryObject, key: str) -> Any: + if key in obj: + return obj[key] + elif "/Parent" in obj: + return _get_inherited( + cast(DictionaryObject, obj["/Parent"].get_object()), key + ) + else: + return None + + try: + # to cope with all types + field = cast(DictionaryObject, field.indirect_reference.get_object()) # type: ignore + except Exception as exc: + raise ValueError("field type is invalid") from exc + if is_null_or_none(_get_inherited(field, "/FT")): + raise ValueError("field is not valid") + ret = [] + if field.get("/Subtype", "") == "/Widget": + if "/P" in field: + ret = [field["/P"].get_object()] + else: + ret = [ + p + for p in self.pages + if field.indirect_reference in p.get("/Annots", "") + ] + else: + kids = field.get("/Kids", ()) + for k in kids: + k = k.get_object() + if (k.get("/Subtype", "") == "/Widget") and ("/T" not in k): + # Kid that is just a widget, not a field: + if "/P" in k: + ret += [k["/P"].get_object()] + else: + ret += [ + p + for p in self.pages + if k.indirect_reference in p.get("/Annots", "") + ] + return [ + x + if isinstance(x, PageObject) + else (self.pages[self._get_page_number_by_indirect(x.indirect_reference)]) # type: ignore + for x in ret + ] + + @property + def open_destination( + self, + ) -> Union[None, Destination, TextStringObject, ByteStringObject]: + """ + Property to access the opening destination (``/OpenAction`` entry in + the PDF catalog). It returns ``None`` if the entry does not exist + or is not set. + + Raises: + Exception: If a destination is invalid. + """ + if "/OpenAction" not in self.root_object: + return None + oa: Any = self.root_object["/OpenAction"] + if isinstance(oa, bytes): # pragma: no cover + oa = oa.decode() + if isinstance(oa, str): + return create_string_object(oa) + elif isinstance(oa, ArrayObject): + try: + page, typ = oa[0:2] + array = oa[2:] + fit = Fit(typ, tuple(array)) + return Destination("OpenAction", page, fit) + except Exception as exc: + raise Exception(f"Invalid Destination {oa}: {exc}") + else: + return None + + @open_destination.setter + def open_destination(self, dest: Union[None, str, Destination, PageObject]) -> None: + raise NotImplementedError("no setter for open_destination") + + @property + def outline(self) -> OutlineType: + """ + Read-only property for the outline present in the document + (i.e., a collection of 'outline items' which are also known as + 'bookmarks'). + """ + return self._get_outline() + + def _get_outline( + self, node: Optional[DictionaryObject] = None, outline: Optional[Any] = None + ) -> OutlineType: + if outline is None: + outline = [] + catalog = self.root_object + + # get the outline dictionary and named destinations + if CO.OUTLINES in catalog: + lines = cast(DictionaryObject, catalog[CO.OUTLINES]) + + if isinstance(lines, NullObject): + return outline + + # §12.3.3 Document outline, entries in the outline dictionary + if not is_null_or_none(lines) and "/First" in lines: + node = cast(DictionaryObject, lines["/First"]) + self._namedDests = self._get_named_destinations() + + if node is None: + return outline + + # see if there are any more outline items + while True: + outline_obj = self._build_outline_item(node) + if outline_obj: + outline.append(outline_obj) + + # check for sub-outline + if "/First" in node: + sub_outline: List[Any] = [] + self._get_outline(cast(DictionaryObject, node["/First"]), sub_outline) + if sub_outline: + outline.append(sub_outline) + + if "/Next" not in node: + break + node = cast(DictionaryObject, node["/Next"]) + + return outline + + @property + def threads(self) -> Optional[ArrayObject]: + """ + Read-only property for the list of threads. + + See §12.4.3 from the PDF 1.7 or 2.0 specification. + + It is an array of dictionaries with "/F" (the first bead in the thread) + and "/I" (a thread information dictionary containing information about + the thread, such as its title, author, and creation date) properties or + None if there are no articles. + + Since PDF 2.0 it can also contain an indirect reference to a metadata + stream containing information about the thread, such as its title, + author, and creation date. + """ + catalog = self.root_object + if CO.THREADS in catalog: + return cast("ArrayObject", catalog[CO.THREADS]) + else: + return None + + @abstractmethod + def _get_page_number_by_indirect( + self, indirect_reference: Union[None, int, NullObject, IndirectObject] + ) -> Optional[int]: + ... # pragma: no cover + + def get_page_number(self, page: PageObject) -> Optional[int]: + """ + Retrieve page number of a given PageObject. + + Args: + page: The page to get page number. Should be + an instance of :class:`PageObject` + + Returns: + The page number or None if page is not found + """ + return self._get_page_number_by_indirect(page.indirect_reference) + + def get_destination_page_number(self, destination: Destination) -> Optional[int]: + """ + Retrieve page number of a given Destination object. + + Args: + destination: The destination to get page number. + + Returns: + The page number or None if page is not found + """ + return self._get_page_number_by_indirect(destination.page) + + def _build_destination( + self, + title: str, + array: Optional[ + List[ + Union[NumberObject, IndirectObject, None, NullObject, DictionaryObject] + ] + ], + ) -> Destination: + page, typ = None, None + # handle outline items with missing or invalid destination + if ( + isinstance(array, (NullObject, str)) + or (isinstance(array, ArrayObject) and len(array) == 0) + or array is None + ): + page = NullObject() + return Destination(title, page, Fit.fit()) + else: + page, typ = array[0:2] # type: ignore + array = array[2:] + try: + return Destination(title, page, Fit(fit_type=typ, fit_args=array)) # type: ignore + except PdfReadError: + logger_warning(f"Unknown destination: {title} {array}", __name__) + if self.strict: + raise + # create a link to first Page + tmp = self.pages[0].indirect_reference + indirect_reference = NullObject() if tmp is None else tmp + return Destination(title, indirect_reference, Fit.fit()) # type: ignore + + def _build_outline_item(self, node: DictionaryObject) -> Optional[Destination]: + dest, title, outline_item = None, None, None + + # title required for valid outline + # § 12.3.3, entries in an outline item dictionary + try: + title = cast("str", node["/Title"]) + except KeyError: + if self.strict: + raise PdfReadError(f"Outline Entry Missing /Title attribute: {node!r}") + title = "" + + if "/A" in node: + # Action, PDFv1.7 Section 12.6 (only type GoTo supported) + action = cast(DictionaryObject, node["/A"]) + action_type = cast(NameObject, action[GoToActionArguments.S]) + if action_type == "/GoTo": + dest = action[GoToActionArguments.D] + elif "/Dest" in node: + # Destination, PDFv1.7 Section 12.3.2 + dest = node["/Dest"] + # if array was referenced in another object, will be a dict w/ key "/D" + if isinstance(dest, DictionaryObject) and "/D" in dest: + dest = dest["/D"] + + if isinstance(dest, ArrayObject): + outline_item = self._build_destination(title, dest) + elif isinstance(dest, str): + # named destination, addresses NameObject Issue #193 + # TODO : keep named destination instead of replacing it ? + try: + outline_item = self._build_destination( + title, self._namedDests[dest].dest_array + ) + except KeyError: + # named destination not found in Name Dict + outline_item = self._build_destination(title, None) + elif dest is None: + # outline item not required to have destination or action + # PDFv1.7 Table 153 + outline_item = self._build_destination(title, dest) + else: + if self.strict: + raise PdfReadError(f"Unexpected destination {dest!r}") + else: + logger_warning( + f"Removed unexpected destination {dest!r} from destination", + __name__, + ) + outline_item = self._build_destination(title, None) + + # if outline item created, add color, format, and child count if present + if outline_item: + if "/C" in node: + # Color of outline item font in (R, G, B) with values ranging 0.0-1.0 + outline_item[NameObject("/C")] = ArrayObject(FloatObject(c) for c in node["/C"]) # type: ignore + if "/F" in node: + # specifies style characteristics bold and/or italic + # with 1=italic, 2=bold, 3=both + outline_item[NameObject("/F")] = node["/F"] + if "/Count" in node: + # absolute value = num. visible children + # with positive = open/unfolded, negative = closed/folded + outline_item[NameObject("/Count")] = node["/Count"] + # if count is 0 we will consider it as open ( in order to have always an is_open to simplify + outline_item[NameObject("/%is_open%")] = BooleanObject( + node.get("/Count", 0) >= 0 + ) + outline_item.node = node + try: + outline_item.indirect_reference = node.indirect_reference + except AttributeError: + pass + return outline_item + + @property + def pages(self) -> List[PageObject]: + """ + Property that emulates a list of :class:`PageObject`. + This property allows to get a page or a range of pages. + + Note: + For PdfWriter only: Provides the capability to remove a page/range of + page from the list (using the del operator). Remember: Only the page + entry is removed, as the objects beneath can be used elsewhere. A + solution to completely remove them - if they are not used anywhere - is + to write to a buffer/temporary file and then load it into a new + PdfWriter. + + """ + return _VirtualList(self.get_num_pages, self.get_page) # type: ignore + + @property + def page_labels(self) -> List[str]: + """ + A list of labels for the pages in this document. + + This property is read-only. The labels are in the order that the pages + appear in the document. + """ + return [page_index2page_label(self, i) for i in range(len(self.pages))] + + @property + def page_layout(self) -> Optional[str]: + """ + Get the page layout currently being used. + + .. list-table:: Valid ``layout`` values + :widths: 50 200 + + * - /NoLayout + - Layout explicitly not specified + * - /SinglePage + - Show one page at a time + * - /OneColumn + - Show one column at a time + * - /TwoColumnLeft + - Show pages in two columns, odd-numbered pages on the left + * - /TwoColumnRight + - Show pages in two columns, odd-numbered pages on the right + * - /TwoPageLeft + - Show two pages at a time, odd-numbered pages on the left + * - /TwoPageRight + - Show two pages at a time, odd-numbered pages on the right + """ + try: + return cast(NameObject, self.root_object[CD.PAGE_LAYOUT]) + except KeyError: + return None + + @property + def page_mode(self) -> Optional[PagemodeType]: + """ + Get the page mode currently being used. + + .. list-table:: Valid ``mode`` values + :widths: 50 200 + + * - /UseNone + - Do not show outline or thumbnails panels + * - /UseOutlines + - Show outline (aka bookmarks) panel + * - /UseThumbs + - Show page thumbnails panel + * - /FullScreen + - Fullscreen view + * - /UseOC + - Show Optional Content Group (OCG) panel + * - /UseAttachments + - Show attachments panel + """ + try: + return self.root_object["/PageMode"] # type: ignore + except KeyError: + return None + + def _flatten( + self, + list_only: bool = False, + pages: Union[None, DictionaryObject, PageObject] = None, + inherit: Optional[Dict[str, Any]] = None, + indirect_reference: Optional[IndirectObject] = None, + ) -> None: + """ + Prepare the document pages to ease searching + + Args: + list_only: Will only list the pages within _flatten_pages. + pages: + inherit: + indirect_reference: Used recursively to flatten the /Pages object. + """ + inheritable_page_attributes = ( + NameObject(PG.RESOURCES), + NameObject(PG.MEDIABOX), + NameObject(PG.CROPBOX), + NameObject(PG.ROTATE), + ) + if inherit is None: + inherit = {} + if pages is None: + # Fix issue 327: set flattened_pages attribute only for + # decrypted file + catalog = self.root_object + pages = catalog["/Pages"].get_object() # type: ignore + assert isinstance(pages, DictionaryObject) + self.flattened_pages = [] + + if PA.TYPE in pages: + t = cast(str, pages[PA.TYPE]) + # if pdf has no type, considered as a page if /Kids is missing + elif PA.KIDS not in pages: + t = "/Page" + else: + t = "/Pages" + + if t == "/Pages": + for attr in inheritable_page_attributes: + if attr in pages: + inherit[attr] = pages[attr] + for page in cast(ArrayObject, pages[PA.KIDS]): + addt = {} + if isinstance(page, IndirectObject): + addt["indirect_reference"] = page + obj = page.get_object() + if obj: + # damaged file may have invalid child in /Pages + try: + self._flatten(list_only, obj, inherit, **addt) + except RecursionError: + raise PdfReadError( + "Maximum recursion depth reached during page flattening." + ) + elif t == "/Page": + for attr_in, value in list(inherit.items()): + # if the page has it's own value, it does not inherit the + # parent's value: + if attr_in not in pages: + pages[attr_in] = value + page_obj = PageObject(self, indirect_reference) + if not list_only: + page_obj.update(pages) + + # TODO: Could flattened_pages be None at this point? + self.flattened_pages.append(page_obj) # type: ignore + + def remove_page( + self, + page: Union[int, PageObject, IndirectObject], + clean: bool = False, + ) -> None: + """ + Remove page from pages list. + + Args: + page: + * :class:`int`: Page number to be removed. + * :class:`~pypdf._page.PageObject`: page to be removed. If the page appears many times + only the first one will be removed. + * :class:`~pypdf.generic.IndirectObject`: Reference to page to be removed. + + clean: replace PageObject with NullObject to prevent annotations + or destinations to reference a detached page. + """ + if self.flattened_pages is None: + self._flatten(self._readonly) + assert self.flattened_pages is not None + if isinstance(page, IndirectObject): + p = page.get_object() + if not isinstance(p, PageObject): + logger_warning("IndirectObject is not referencing a page", __name__) + return + page = p + + if not isinstance(page, int): + try: + page = self.flattened_pages.index(page) + except ValueError: + logger_warning("Cannot find page in pages", __name__) + return + if not (0 <= page < len(self.flattened_pages)): + logger_warning("Page number is out of range", __name__) + return + + ind = self.pages[page].indirect_reference + del self.pages[page] + if clean and ind is not None: + self._replace_object(ind, NullObject()) + + def _get_indirect_object(self, num: int, gen: int) -> Optional[PdfObject]: + """ + Used to ease development. + + This is equivalent to generic.IndirectObject(num,gen,self).get_object() + + Args: + num: The object number of the indirect object. + gen: The generation number of the indirect object. + + Returns: + A PdfObject + """ + return IndirectObject(num, gen, self).get_object() + + def decode_permissions( + self, permissions_code: int + ) -> Dict[str, bool]: # pragma: no cover + """Take the permissions as an integer, return the allowed access.""" + deprecate_with_replacement( + old_name="decode_permissions", + new_name="user_access_permissions", + removed_in="5.0.0", + ) + + permissions_mapping = { + "print": UserAccessPermissions.PRINT, + "modify": UserAccessPermissions.MODIFY, + "copy": UserAccessPermissions.EXTRACT, + "annotations": UserAccessPermissions.ADD_OR_MODIFY, + "forms": UserAccessPermissions.FILL_FORM_FIELDS, + # Do not fix typo, as part of official, but deprecated API. + "accessability": UserAccessPermissions.EXTRACT_TEXT_AND_GRAPHICS, + "assemble": UserAccessPermissions.ASSEMBLE_DOC, + "print_high_quality": UserAccessPermissions.PRINT_TO_REPRESENTATION, + } + + return { + key: permissions_code & flag != 0 + for key, flag in permissions_mapping.items() + } + + @property + def user_access_permissions(self) -> Optional[UserAccessPermissions]: + """Get the user access permissions for encrypted documents. Returns None if not encrypted.""" + if self._encryption is None: + return None + return UserAccessPermissions(self._encryption.P) + + @property + @abstractmethod + def is_encrypted(self) -> bool: + """ + Read-only boolean property showing whether this PDF file is encrypted. + + Note that this property, if true, will remain true even after the + :meth:`decrypt()` method is called. + """ + ... # pragma: no cover + + @property + def xfa(self) -> Optional[Dict[str, Any]]: + tree: Optional[TreeObject] = None + retval: Dict[str, Any] = {} + catalog = self.root_object + + if "/AcroForm" not in catalog or not catalog["/AcroForm"]: + return None + + tree = cast(TreeObject, catalog["/AcroForm"]) + + if "/XFA" in tree: + fields = cast(ArrayObject, tree["/XFA"]) + i = iter(fields) + for f in i: + tag = f + f = next(i) + if isinstance(f, IndirectObject): + field = cast(Optional[EncodedStreamObject], f.get_object()) + if field: + es = zlib.decompress(field._data) + retval[tag] = es + return retval + + @property + def attachments(self) -> Mapping[str, List[bytes]]: + return LazyDict( + { + name: (self._get_attachment_list, name) + for name in self._list_attachments() + } + ) + + def _list_attachments(self) -> List[str]: + """ + Retrieves the list of filenames of file attachments. + + Returns: + list of filenames + """ + catalog = self.root_object + # From the catalog get the embedded file names + try: + filenames = cast( + ArrayObject, + cast( + DictionaryObject, + cast(DictionaryObject, catalog["/Names"])["/EmbeddedFiles"], + )["/Names"], + ) + except KeyError: + return [] + attachments_names = [f for f in filenames if isinstance(f, str)] + return attachments_names + + def _get_attachment_list(self, name: str) -> List[bytes]: + out = self._get_attachments(name)[name] + if isinstance(out, list): + return out + return [out] + + def _get_attachments( + self, filename: Optional[str] = None + ) -> Dict[str, Union[bytes, List[bytes]]]: + """ + Retrieves all or selected file attachments of the PDF as a dictionary of file names + and the file data as a bytestring. + + Args: + filename: If filename is None, then a dictionary of all attachments + will be returned, where the key is the filename and the value + is the content. Otherwise, a dictionary with just a single key + - the filename - and its content will be returned. + + Returns: + dictionary of filename -> Union[bytestring or List[ByteString]] + If the filename exists multiple times a list of the different versions will be provided. + """ + catalog = self.root_object + # From the catalog get the embedded file names + try: + filenames = cast( + ArrayObject, + cast( + DictionaryObject, + cast(DictionaryObject, catalog["/Names"])["/EmbeddedFiles"], + )["/Names"], + ) + except KeyError: + return {} + attachments: Dict[str, Union[bytes, List[bytes]]] = {} + # Loop through attachments + for i in range(len(filenames)): + f = filenames[i] + if isinstance(f, str): + if filename is not None and f != filename: + continue + name = f + f_dict = filenames[i + 1].get_object() + f_data = f_dict["/EF"]["/F"].get_data() + if name in attachments: + if not isinstance(attachments[name], list): + attachments[name] = [attachments[name]] # type:ignore + attachments[name].append(f_data) # type:ignore + else: + attachments[name] = f_data + return attachments + + +class LazyDict(Mapping[Any, Any]): + def __init__(self, *args: Any, **kw: Any) -> None: + self._raw_dict = dict(*args, **kw) + + def __getitem__(self, key: str) -> Any: + func, arg = self._raw_dict.__getitem__(key) + return func(arg) + + def __iter__(self) -> Iterator[Any]: + return iter(self._raw_dict) + + def __len__(self) -> int: + return len(self._raw_dict) + + def __str__(self) -> str: + return f"LazyDict(keys={list(self.keys())})" diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_encryption.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_encryption.py new file mode 100644 index 00000000..e5cdd932 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_encryption.py @@ -0,0 +1,1168 @@ +# Copyright (c) 2022, exiledkingcc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +import hashlib +import secrets +import struct +from enum import Enum, IntEnum +from typing import Any, Dict, Optional, Tuple, Union, cast + +from pypdf._crypt_providers import ( + CryptAES, + CryptBase, + CryptIdentity, + CryptRC4, + aes_cbc_decrypt, + aes_cbc_encrypt, + aes_ecb_decrypt, + aes_ecb_encrypt, + rc4_decrypt, + rc4_encrypt, +) + +from ._utils import logger_warning +from .generic import ( + ArrayObject, + ByteStringObject, + DictionaryObject, + NameObject, + NumberObject, + PdfObject, + StreamObject, + TextStringObject, + create_string_object, +) + + +class CryptFilter: + def __init__( + self, + stm_crypt: CryptBase, + str_crypt: CryptBase, + ef_crypt: CryptBase, + ) -> None: + self.stm_crypt = stm_crypt + self.str_crypt = str_crypt + self.ef_crypt = ef_crypt + + def encrypt_object(self, obj: PdfObject) -> PdfObject: + if isinstance(obj, ByteStringObject): + data = self.str_crypt.encrypt(obj.original_bytes) + obj = ByteStringObject(data) + if isinstance(obj, TextStringObject): + data = self.str_crypt.encrypt(obj.get_encoded_bytes()) + obj = ByteStringObject(data) + elif isinstance(obj, StreamObject): + obj2 = StreamObject() + obj2.update(obj) + obj2.set_data(self.stm_crypt.encrypt(obj._data)) + for key, value in obj.items(): # Dont forget the Stream dict. + obj2[key] = self.encrypt_object(value) + obj = obj2 + elif isinstance(obj, DictionaryObject): + obj2 = DictionaryObject() # type: ignore + for key, value in obj.items(): + obj2[key] = self.encrypt_object(value) + obj = obj2 + elif isinstance(obj, ArrayObject): + obj = ArrayObject(self.encrypt_object(x) for x in obj) + return obj + + def decrypt_object(self, obj: PdfObject) -> PdfObject: + if isinstance(obj, (ByteStringObject, TextStringObject)): + data = self.str_crypt.decrypt(obj.original_bytes) + obj = create_string_object(data) + elif isinstance(obj, StreamObject): + obj._data = self.stm_crypt.decrypt(obj._data) + for key, value in obj.items(): # Dont forget the Stream dict. + obj[key] = self.decrypt_object(value) + elif isinstance(obj, DictionaryObject): + for key, value in obj.items(): + obj[key] = self.decrypt_object(value) + elif isinstance(obj, ArrayObject): + for i in range(len(obj)): + obj[i] = self.decrypt_object(obj[i]) + return obj + + +_PADDING = ( + b"\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08" + b"\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a" +) + + +def _padding(data: bytes) -> bytes: + return (data + _PADDING)[:32] + + +class AlgV4: + @staticmethod + def compute_key( + password: bytes, + rev: int, + key_size: int, + o_entry: bytes, + P: int, + id1_entry: bytes, + metadata_encrypted: bool, + ) -> bytes: + """ + Algorithm 2: Computing an encryption key. + + a) Pad or truncate the password string to exactly 32 bytes. If the + password string is more than 32 bytes long, + use only its first 32 bytes; if it is less than 32 bytes long, pad it + by appending the required number of + additional bytes from the beginning of the following padding string: + < 28 BF 4E 5E 4E 75 8A 41 64 00 4E 56 FF FA 01 08 + 2E 2E 00 B6 D0 68 3E 80 2F 0C A9 FE 64 53 69 7A > + That is, if the password string is n bytes long, append + the first 32 - n bytes of the padding string to the end + of the password string. If the password string is empty + (zero-length), meaning there is no user password, + substitute the entire padding string in its place. + + b) Initialize the MD5 hash function and pass the result of step (a) + as input to this function. + c) Pass the value of the encryption dictionary’s O entry to the + MD5 hash function. ("Algorithm 3: Computing + the encryption dictionary’s O (owner password) value" shows how the + O value is computed.) + d) Convert the integer value of the P entry to a 32-bit unsigned binary + number and pass these bytes to the + MD5 hash function, low-order byte first. + e) Pass the first element of the file’s file identifier array (the value + of the ID entry in the document’s trailer + dictionary; see Table 15) to the MD5 hash function. + f) (Security handlers of revision 4 or greater) If document metadata is + not being encrypted, pass 4 bytes with + the value 0xFFFFFFFF to the MD5 hash function. + g) Finish the hash. + h) (Security handlers of revision 3 or greater) Do the following + 50 times: Take the output from the previous + MD5 hash and pass the first n bytes of the output as input into a new + MD5 hash, where n is the number of + bytes of the encryption key as defined by the value of the encryption + dictionary’s Length entry. + i) Set the encryption key to the first n bytes of the output from the + final MD5 hash, where n shall always be 5 + for security handlers of revision 2 but, for security handlers of + revision 3 or greater, shall depend on the + value of the encryption dictionary’s Length entry. + + Args: + password: The encryption secret as a bytes-string + rev: The encryption revision (see PDF standard) + key_size: The size of the key in bytes + o_entry: The owner entry + P: A set of flags specifying which operations shall be permitted + when the document is opened with user access. If bit 2 is set to 1, + all other bits are ignored and all operations are permitted. + If bit 2 is set to 0, permission for operations are based on the + values of the remaining flags defined in Table 24. + id1_entry: + metadata_encrypted: A boolean indicating if the metadata is encrypted. + + Returns: + The u_hash digest of length key_size + """ + a = _padding(password) + u_hash = hashlib.md5(a) + u_hash.update(o_entry) + u_hash.update(struct.pack("= 4 and not metadata_encrypted: + u_hash.update(b"\xff\xff\xff\xff") + u_hash_digest = u_hash.digest() + length = key_size // 8 + if rev >= 3: + for _ in range(50): + u_hash_digest = hashlib.md5(u_hash_digest[:length]).digest() + return u_hash_digest[:length] + + @staticmethod + def compute_O_value_key(owner_password: bytes, rev: int, key_size: int) -> bytes: + """ + Algorithm 3: Computing the encryption dictionary’s O (owner password) value. + + a) Pad or truncate the owner password string as described in step (a) + of "Algorithm 2: Computing an encryption key". + If there is no owner password, use the user password instead. + b) Initialize the MD5 hash function and pass the result of step (a) as + input to this function. + c) (Security handlers of revision 3 or greater) Do the following 50 times: + Take the output from the previous + MD5 hash and pass it as input into a new MD5 hash. + d) Create an RC4 encryption key using the first n bytes of the output + from the final MD5 hash, where n shall + always be 5 for security handlers of revision 2 but, for security + handlers of revision 3 or greater, shall + depend on the value of the encryption dictionary’s Length entry. + e) Pad or truncate the user password string as described in step (a) of + "Algorithm 2: Computing an encryption key". + f) Encrypt the result of step (e), using an RC4 encryption function with + the encryption key obtained in step (d). + g) (Security handlers of revision 3 or greater) Do the following 19 times: + Take the output from the previous + invocation of the RC4 function and pass it as input to a new + invocation of the function; use an encryption + key generated by taking each byte of the encryption key obtained in + step (d) and performing an XOR + (exclusive or) operation between that byte and the single-byte value + of the iteration counter (from 1 to 19). + h) Store the output from the final invocation of the RC4 function as + the value of the O entry in the encryption dictionary. + + Args: + owner_password: + rev: The encryption revision (see PDF standard) + key_size: The size of the key in bytes + + Returns: + The RC4 key + """ + a = _padding(owner_password) + o_hash_digest = hashlib.md5(a).digest() + + if rev >= 3: + for _ in range(50): + o_hash_digest = hashlib.md5(o_hash_digest).digest() + + rc4_key = o_hash_digest[: key_size // 8] + return rc4_key + + @staticmethod + def compute_O_value(rc4_key: bytes, user_password: bytes, rev: int) -> bytes: + """ + See :func:`compute_O_value_key`. + + Args: + rc4_key: + user_password: + rev: The encryption revision (see PDF standard) + + Returns: + The RC4 encrypted + """ + a = _padding(user_password) + rc4_enc = rc4_encrypt(rc4_key, a) + if rev >= 3: + for i in range(1, 20): + key = bytes(bytearray(x ^ i for x in rc4_key)) + rc4_enc = rc4_encrypt(key, rc4_enc) + return rc4_enc + + @staticmethod + def compute_U_value(key: bytes, rev: int, id1_entry: bytes) -> bytes: + """ + Algorithm 4: Computing the encryption dictionary’s U (user password) value. + + (Security handlers of revision 2) + + a) Create an encryption key based on the user password string, as + described in "Algorithm 2: Computing an encryption key". + b) Encrypt the 32-byte padding string shown in step (a) of + "Algorithm 2: Computing an encryption key", using an RC4 encryption + function with the encryption key from the preceding step. + c) Store the result of step (b) as the value of the U entry in the + encryption dictionary. + + Args: + key: + rev: The encryption revision (see PDF standard) + id1_entry: + + Returns: + The value + """ + if rev <= 2: + value = rc4_encrypt(key, _PADDING) + return value + + """ + Algorithm 5: Computing the encryption dictionary’s U (user password) value. + + (Security handlers of revision 3 or greater) + + a) Create an encryption key based on the user password string, as + described in "Algorithm 2: Computing an encryption key". + b) Initialize the MD5 hash function and pass the 32-byte padding string + shown in step (a) of "Algorithm 2: + Computing an encryption key" as input to this function. + c) Pass the first element of the file’s file identifier array (the value + of the ID entry in the document’s trailer + dictionary; see Table 15) to the hash function and finish the hash. + d) Encrypt the 16-byte result of the hash, using an RC4 encryption + function with the encryption key from step (a). + e) Do the following 19 times: Take the output from the previous + invocation of the RC4 function and pass it as input to a new + invocation of the function; use an encryption key generated by + taking each byte of the original encryption key obtained in + step (a) and performing an XOR (exclusive or) operation between that + byte and the single-byte value of the iteration counter (from 1 to 19). + f) Append 16 bytes of arbitrary padding to the output from the final + invocation of the RC4 function and store the 32-byte result as the + value of the U entry in the encryption dictionary. + """ + u_hash = hashlib.md5(_PADDING) + u_hash.update(id1_entry) + rc4_enc = rc4_encrypt(key, u_hash.digest()) + for i in range(1, 20): + rc4_key = bytes(bytearray(x ^ i for x in key)) + rc4_enc = rc4_encrypt(rc4_key, rc4_enc) + return _padding(rc4_enc) + + @staticmethod + def verify_user_password( + user_password: bytes, + rev: int, + key_size: int, + o_entry: bytes, + u_entry: bytes, + P: int, + id1_entry: bytes, + metadata_encrypted: bool, + ) -> bytes: + """ + Algorithm 6: Authenticating the user password. + + a) Perform all but the last step of "Algorithm 4: Computing the + encryption dictionary’s U (user password) value (Security handlers of + revision 2)" or "Algorithm 5: Computing the encryption dictionary’s U + (user password) value (Security handlers of revision 3 or greater)" + using the supplied password string. + b) If the result of step (a) is equal to the value of the encryption + dictionary’s U entry (comparing on the first 16 bytes in the case of + security handlers of revision 3 or greater), the password supplied is + the correct user password. The key obtained in step (a) (that is, in + the first step of "Algorithm 4: Computing the encryption + dictionary’s U (user password) value + (Security handlers of revision 2)" or + "Algorithm 5: Computing the encryption dictionary’s U (user password) + value (Security handlers of revision 3 or greater)") shall be used + to decrypt the document. + + Args: + user_password: The user password as a bytes stream + rev: The encryption revision (see PDF standard) + key_size: The size of the key in bytes + o_entry: The owner entry + u_entry: The user entry + P: A set of flags specifying which operations shall be permitted + when the document is opened with user access. If bit 2 is set to 1, + all other bits are ignored and all operations are permitted. + If bit 2 is set to 0, permission for operations are based on the + values of the remaining flags defined in Table 24. + id1_entry: + metadata_encrypted: A boolean indicating if the metadata is encrypted. + + Returns: + The key + """ + key = AlgV4.compute_key( + user_password, rev, key_size, o_entry, P, id1_entry, metadata_encrypted + ) + u_value = AlgV4.compute_U_value(key, rev, id1_entry) + if rev >= 3: + u_value = u_value[:16] + u_entry = u_entry[:16] + if u_value != u_entry: + key = b"" + return key + + @staticmethod + def verify_owner_password( + owner_password: bytes, + rev: int, + key_size: int, + o_entry: bytes, + u_entry: bytes, + P: int, + id1_entry: bytes, + metadata_encrypted: bool, + ) -> bytes: + """ + Algorithm 7: Authenticating the owner password. + + a) Compute an encryption key from the supplied password string, as + described in steps (a) to (d) of + "Algorithm 3: Computing the encryption dictionary’s O (owner password) + value". + b) (Security handlers of revision 2 only) Decrypt the value of the + encryption dictionary’s O entry, using an RC4 + encryption function with the encryption key computed in step (a). + (Security handlers of revision 3 or greater) Do the following 20 times: + Decrypt the value of the encryption dictionary’s O entry (first iteration) + or the output from the previous iteration (all subsequent iterations), + using an RC4 encryption function with a different encryption key at + each iteration. The key shall be generated by taking the original key + (obtained in step (a)) and performing an XOR (exclusive or) operation + between each byte of the key and the single-byte value of the + iteration counter (from 19 to 0). + c) The result of step (b) purports to be the user password. + Authenticate this user password using + "Algorithm 6: Authenticating the user password". + If it is correct, the password supplied is the correct owner password. + + Args: + owner_password: + rev: The encryption revision (see PDF standard) + key_size: The size of the key in bytes + o_entry: The owner entry + u_entry: The user entry + P: A set of flags specifying which operations shall be permitted + when the document is opened with user access. If bit 2 is set to 1, + all other bits are ignored and all operations are permitted. + If bit 2 is set to 0, permission for operations are based on the + values of the remaining flags defined in Table 24. + id1_entry: + metadata_encrypted: A boolean indicating if the metadata is encrypted. + + Returns: + bytes + """ + rc4_key = AlgV4.compute_O_value_key(owner_password, rev, key_size) + + if rev <= 2: + user_password = rc4_decrypt(rc4_key, o_entry) + else: + user_password = o_entry + for i in range(19, -1, -1): + key = bytes(bytearray(x ^ i for x in rc4_key)) + user_password = rc4_decrypt(key, user_password) + return AlgV4.verify_user_password( + user_password, + rev, + key_size, + o_entry, + u_entry, + P, + id1_entry, + metadata_encrypted, + ) + + +class AlgV5: + @staticmethod + def verify_owner_password( + R: int, password: bytes, o_value: bytes, oe_value: bytes, u_value: bytes + ) -> bytes: + """ + Algorithm 3.2a Computing an encryption key. + + To understand the algorithm below, it is necessary to treat the O and U + strings in the Encrypt dictionary as made up of three sections. + The first 32 bytes are a hash value (explained below). The next 8 bytes + are called the Validation Salt. The final 8 bytes are called the Key Salt. + + 1. The password string is generated from Unicode input by processing the + input string with the SASLprep (IETF RFC 4013) profile of + stringprep (IETF RFC 3454), and then converting to a UTF-8 + representation. + 2. Truncate the UTF-8 representation to 127 bytes if it is longer than + 127 bytes. + 3. Test the password against the owner key by computing the SHA-256 hash + of the UTF-8 password concatenated with the 8 bytes of owner + Validation Salt, concatenated with the 48-byte U string. If the + 32-byte result matches the first 32 bytes of the O string, this is + the owner password. + Compute an intermediate owner key by computing the SHA-256 hash of + the UTF-8 password concatenated with the 8 bytes of owner Key Salt, + concatenated with the 48-byte U string. The 32-byte result is the + key used to decrypt the 32-byte OE string using AES-256 in CBC mode + with no padding and an initialization vector of zero. + The 32-byte result is the file encryption key. + 4. Test the password against the user key by computing the SHA-256 hash + of the UTF-8 password concatenated with the 8 bytes of user + Validation Salt. If the 32 byte result matches the first 32 bytes of + the U string, this is the user password. + Compute an intermediate user key by computing the SHA-256 hash of the + UTF-8 password concatenated with the 8 bytes of user Key Salt. + The 32-byte result is the key used to decrypt the 32-byte + UE string using AES-256 in CBC mode with no padding and an + initialization vector of zero. The 32-byte result is the file + encryption key. + 5. Decrypt the 16-byte Perms string using AES-256 in ECB mode with an + initialization vector of zero and the file encryption key as the key. + Verify that bytes 9-11 of the result are the characters ‘a’, ‘d’, ‘b’. + Bytes 0-3 of the decrypted Perms entry, treated as a little-endian + integer, are the user permissions. + They should match the value in the P key. + + Args: + R: A number specifying which revision of the standard security + handler shall be used to interpret this dictionary + password: The owner password + o_value: A 32-byte string, based on both the owner and user passwords, + that shall be used in computing the encryption key and in + determining whether a valid owner password was entered + oe_value: + u_value: A 32-byte string, based on the user password, that shall be + used in determining whether to prompt the user for a password and, + if so, whether a valid user or owner password was entered. + + Returns: + The key + """ + password = password[:127] + if ( + AlgV5.calculate_hash(R, password, o_value[32:40], u_value[:48]) + != o_value[:32] + ): + return b"" + iv = bytes(0 for _ in range(16)) + tmp_key = AlgV5.calculate_hash(R, password, o_value[40:48], u_value[:48]) + key = aes_cbc_decrypt(tmp_key, iv, oe_value) + return key + + @staticmethod + def verify_user_password( + R: int, password: bytes, u_value: bytes, ue_value: bytes + ) -> bytes: + """ + See :func:`verify_owner_password`. + + Args: + R: A number specifying which revision of the standard security + handler shall be used to interpret this dictionary + password: The user password + u_value: A 32-byte string, based on the user password, that shall be + used in determining whether to prompt the user for a password + and, if so, whether a valid user or owner password was entered. + ue_value: + + Returns: + bytes + """ + password = password[:127] + if AlgV5.calculate_hash(R, password, u_value[32:40], b"") != u_value[:32]: + return b"" + iv = bytes(0 for _ in range(16)) + tmp_key = AlgV5.calculate_hash(R, password, u_value[40:48], b"") + return aes_cbc_decrypt(tmp_key, iv, ue_value) + + @staticmethod + def calculate_hash(R: int, password: bytes, salt: bytes, udata: bytes) -> bytes: + # from https://github.com/qpdf/qpdf/blob/main/libqpdf/QPDF_encryption.cc + k = hashlib.sha256(password + salt + udata).digest() + if R < 6: + return k + count = 0 + while True: + count += 1 + k1 = password + k + udata + e = aes_cbc_encrypt(k[:16], k[16:32], k1 * 64) + hash_fn = ( + hashlib.sha256, + hashlib.sha384, + hashlib.sha512, + )[sum(e[:16]) % 3] + k = hash_fn(e).digest() + if count >= 64 and e[-1] <= count - 32: + break + return k[:32] + + @staticmethod + def verify_perms( + key: bytes, perms: bytes, p: int, metadata_encrypted: bool + ) -> bool: + """ + See :func:`verify_owner_password` and :func:`compute_perms_value`. + + Args: + key: The owner password + perms: + p: A set of flags specifying which operations shall be permitted + when the document is opened with user access. + If bit 2 is set to 1, all other bits are ignored and all + operations are permitted. + If bit 2 is set to 0, permission for operations are based on + the values of the remaining flags defined in Table 24. + metadata_encrypted: + + Returns: + A boolean + """ + b8 = b"T" if metadata_encrypted else b"F" + p1 = struct.pack(" Dict[Any, Any]: + user_password = user_password[:127] + owner_password = owner_password[:127] + u_value, ue_value = AlgV5.compute_U_value(R, user_password, key) + o_value, oe_value = AlgV5.compute_O_value(R, owner_password, key, u_value) + perms = AlgV5.compute_Perms_value(key, p, metadata_encrypted) + return { + "/U": u_value, + "/UE": ue_value, + "/O": o_value, + "/OE": oe_value, + "/Perms": perms, + } + + @staticmethod + def compute_U_value(R: int, password: bytes, key: bytes) -> Tuple[bytes, bytes]: + """ + Algorithm 3.8 Computing the encryption dictionary’s U (user password) + and UE (user encryption key) values. + + 1. Generate 16 random bytes of data using a strong random number generator. + The first 8 bytes are the User Validation Salt. The second 8 bytes + are the User Key Salt. Compute the 32-byte SHA-256 hash of the + password concatenated with the User Validation Salt. The 48-byte + string consisting of the 32-byte hash followed by the User + Validation Salt followed by the User Key Salt is stored as the U key. + 2. Compute the 32-byte SHA-256 hash of the password concatenated with + the User Key Salt. Using this hash as the key, encrypt the file + encryption key using AES-256 in CBC mode with no padding and an + initialization vector of zero. The resulting 32-byte string is stored + as the UE key. + + Args: + R: + password: + key: + + Returns: + A tuple (u-value, ue value) + """ + random_bytes = secrets.token_bytes(16) + val_salt = random_bytes[:8] + key_salt = random_bytes[8:] + u_value = AlgV5.calculate_hash(R, password, val_salt, b"") + val_salt + key_salt + + tmp_key = AlgV5.calculate_hash(R, password, key_salt, b"") + iv = bytes(0 for _ in range(16)) + ue_value = aes_cbc_encrypt(tmp_key, iv, key) + return u_value, ue_value + + @staticmethod + def compute_O_value( + R: int, password: bytes, key: bytes, u_value: bytes + ) -> Tuple[bytes, bytes]: + """ + Algorithm 3.9 Computing the encryption dictionary’s O (owner password) + and OE (owner encryption key) values. + + 1. Generate 16 random bytes of data using a strong random number + generator. The first 8 bytes are the Owner Validation Salt. The + second 8 bytes are the Owner Key Salt. Compute the 32-byte SHA-256 + hash of the password concatenated with the Owner Validation Salt and + then concatenated with the 48-byte U string as generated in + Algorithm 3.8. The 48-byte string consisting of the 32-byte hash + followed by the Owner Validation Salt followed by the Owner Key Salt + is stored as the O key. + 2. Compute the 32-byte SHA-256 hash of the password concatenated with + the Owner Key Salt and then concatenated with the 48-byte U string as + generated in Algorithm 3.8. Using this hash as the key, + encrypt the file encryption key using AES-256 in CBC mode with + no padding and an initialization vector of zero. + The resulting 32-byte string is stored as the OE key. + + Args: + R: + password: + key: + u_value: A 32-byte string, based on the user password, that shall be + used in determining whether to prompt the user for a password + and, if so, whether a valid user or owner password was entered. + + Returns: + A tuple (O value, OE value) + """ + random_bytes = secrets.token_bytes(16) + val_salt = random_bytes[:8] + key_salt = random_bytes[8:] + o_value = ( + AlgV5.calculate_hash(R, password, val_salt, u_value) + val_salt + key_salt + ) + tmp_key = AlgV5.calculate_hash(R, password, key_salt, u_value[:48]) + iv = bytes(0 for _ in range(16)) + oe_value = aes_cbc_encrypt(tmp_key, iv, key) + return o_value, oe_value + + @staticmethod + def compute_Perms_value(key: bytes, p: int, metadata_encrypted: bool) -> bytes: + """ + Algorithm 3.10 Computing the encryption dictionary’s Perms + (permissions) value. + + 1. Extend the permissions (contents of the P integer) to 64 bits by + setting the upper 32 bits to all 1’s. + (This allows for future extension without changing the format.) + 2. Record the 8 bytes of permission in the bytes 0-7 of the block, + low order byte first. + 3. Set byte 8 to the ASCII value ' T ' or ' F ' according to the + EncryptMetadata Boolean. + 4. Set bytes 9-11 to the ASCII characters ' a ', ' d ', ' b '. + 5. Set bytes 12-15 to 4 bytes of random data, which will be ignored. + 6. Encrypt the 16-byte block using AES-256 in ECB mode with an + initialization vector of zero, using the file encryption key as the + key. The result (16 bytes) is stored as the Perms string, and checked + for validity when the file is opened. + + Args: + key: + p: A set of flags specifying which operations shall be permitted + when the document is opened with user access. If bit 2 is set to 1, + all other bits are ignored and all operations are permitted. + If bit 2 is set to 0, permission for operations are based on the + values of the remaining flags defined in Table 24. + metadata_encrypted: A boolean indicating if the metadata is encrypted. + + Returns: + The perms value + """ + b8 = b"T" if metadata_encrypted else b"F" + rr = secrets.token_bytes(4) + data = struct.pack(" None: + # §7.6.2, entries common to all encryption dictionaries + # use same name as keys of encryption dictionaries entries + self.V = V + self.R = R + self.Length = Length # key_size + self.P = (P + 0x100000000) % 0x100000000 # maybe P < 0 + self.EncryptMetadata = EncryptMetadata + self.id1_entry = first_id_entry + self.StmF = StmF + self.StrF = StrF + self.EFF = EFF + self.values: EncryptionValues = values if values else EncryptionValues() + + self._password_type = PasswordType.NOT_DECRYPTED + self._key: Optional[bytes] = None + + def is_decrypted(self) -> bool: + return self._password_type != PasswordType.NOT_DECRYPTED + + def encrypt_object(self, obj: PdfObject, idnum: int, generation: int) -> PdfObject: + # skip calculate key + if not self._is_encryption_object(obj): + return obj + + cf = self._make_crypt_filter(idnum, generation) + return cf.encrypt_object(obj) + + def decrypt_object(self, obj: PdfObject, idnum: int, generation: int) -> PdfObject: + # skip calculate key + if not self._is_encryption_object(obj): + return obj + + cf = self._make_crypt_filter(idnum, generation) + return cf.decrypt_object(obj) + + @staticmethod + def _is_encryption_object(obj: PdfObject) -> bool: + return isinstance( + obj, + ( + ByteStringObject, + TextStringObject, + StreamObject, + ArrayObject, + DictionaryObject, + ), + ) + + def _make_crypt_filter(self, idnum: int, generation: int) -> CryptFilter: + """ + Algorithm 1: Encryption of data using the RC4 or AES algorithms. + + a) Obtain the object number and generation number from the object + identifier of the string or stream to be encrypted + (see 7.3.10, "Indirect Objects"). If the string is a direct object, + use the identifier of the indirect object containing it. + b) For all strings and streams without crypt filter specifier; treating + the object number and generation number as binary integers, extend + the original n-byte encryption key to n + 5 bytes by appending the + low-order 3 bytes of the object number and the low-order 2 bytes of + the generation number in that order, low-order byte first. + (n is 5 unless the value of V in the encryption dictionary is greater + than 1, in which case n is the value of Length divided by 8.) + If using the AES algorithm, extend the encryption key an additional + 4 bytes by adding the value “sAlT”, which corresponds to the + hexadecimal values 0x73, 0x41, 0x6C, 0x54. (This addition is done for + backward compatibility and is not intended to provide additional + security.) + c) Initialize the MD5 hash function and pass the result of step (b) as + input to this function. + d) Use the first (n + 5) bytes, up to a maximum of 16, of the output + from the MD5 hash as the key for the RC4 or AES symmetric key + algorithms, along with the string or stream data to be encrypted. + If using the AES algorithm, the Cipher Block Chaining (CBC) mode, + which requires an initialization vector, is used. The block size + parameter is set to 16 bytes, and the initialization vector is a + 16-byte random number that is stored as the first 16 bytes of the + encrypted stream or string. + + Algorithm 3.1a Encryption of data using the AES algorithm + 1. Use the 32-byte file encryption key for the AES-256 symmetric key + algorithm, along with the string or stream data to be encrypted. + Use the AES algorithm in Cipher Block Chaining (CBC) mode, which + requires an initialization vector. The block size parameter is set to + 16 bytes, and the initialization vector is a 16-byte random number + that is stored as the first 16 bytes of the encrypted stream or string. + The output is the encrypted data to be stored in the PDF file. + """ + pack1 = struct.pack(" CryptBase: + if method == "/AESV3": + return CryptAES(aes256_key) + if method == "/AESV2": + return CryptAES(aes128_key) + elif method == "/Identity": + return CryptIdentity() + else: + return CryptRC4(rc4_key) + + @staticmethod + def _encode_password(password: Union[bytes, str]) -> bytes: + if isinstance(password, str): + try: + pwd = password.encode("latin-1") + except Exception: + pwd = password.encode("utf-8") + else: + pwd = password + return pwd + + def verify(self, password: Union[bytes, str]) -> PasswordType: + pwd = self._encode_password(password) + key, rc = self.verify_v4(pwd) if self.V <= 4 else self.verify_v5(pwd) + if rc != PasswordType.NOT_DECRYPTED: + self._password_type = rc + self._key = key + return rc + + def verify_v4(self, password: bytes) -> Tuple[bytes, PasswordType]: + # verify owner password first + key = AlgV4.verify_owner_password( + password, + self.R, + self.Length, + self.values.O, + self.values.U, + self.P, + self.id1_entry, + self.EncryptMetadata, + ) + if key: + return key, PasswordType.OWNER_PASSWORD + key = AlgV4.verify_user_password( + password, + self.R, + self.Length, + self.values.O, + self.values.U, + self.P, + self.id1_entry, + self.EncryptMetadata, + ) + if key: + return key, PasswordType.USER_PASSWORD + return b"", PasswordType.NOT_DECRYPTED + + def verify_v5(self, password: bytes) -> Tuple[bytes, PasswordType]: + # TODO: use SASLprep process + # verify owner password first + key = AlgV5.verify_owner_password( + self.R, password, self.values.O, self.values.OE, self.values.U + ) + rc = PasswordType.OWNER_PASSWORD + if not key: + key = AlgV5.verify_user_password( + self.R, password, self.values.U, self.values.UE + ) + rc = PasswordType.USER_PASSWORD + if not key: + return b"", PasswordType.NOT_DECRYPTED + + # verify Perms + if not AlgV5.verify_perms(key, self.values.Perms, self.P, self.EncryptMetadata): + logger_warning("ignore '/Perms' verify failed", __name__) + return key, rc + + def write_entry( + self, user_password: str, owner_password: Optional[str] + ) -> DictionaryObject: + user_pwd = self._encode_password(user_password) + owner_pwd = self._encode_password(owner_password) if owner_password else None + if owner_pwd is None: + owner_pwd = user_pwd + + if self.V <= 4: + self.compute_values_v4(user_pwd, owner_pwd) + else: + self._key = secrets.token_bytes(self.Length // 8) + values = AlgV5.generate_values( + self.R, user_pwd, owner_pwd, self._key, self.P, self.EncryptMetadata + ) + self.values.O = values["/O"] + self.values.U = values["/U"] + self.values.OE = values["/OE"] + self.values.UE = values["/UE"] + self.values.Perms = values["/Perms"] + + dict_obj = DictionaryObject() + dict_obj[NameObject("/V")] = NumberObject(self.V) + dict_obj[NameObject("/R")] = NumberObject(self.R) + dict_obj[NameObject("/Length")] = NumberObject(self.Length) + dict_obj[NameObject("/P")] = NumberObject(self.P) + dict_obj[NameObject("/Filter")] = NameObject("/Standard") + # ignore /EncryptMetadata + + dict_obj[NameObject("/O")] = ByteStringObject(self.values.O) + dict_obj[NameObject("/U")] = ByteStringObject(self.values.U) + + if self.V >= 4: + # TODO: allow different method + std_cf = DictionaryObject() + std_cf[NameObject("/AuthEvent")] = NameObject("/DocOpen") + std_cf[NameObject("/CFM")] = NameObject(self.StmF) + std_cf[NameObject("/Length")] = NumberObject(self.Length // 8) + cf = DictionaryObject() + cf[NameObject("/StdCF")] = std_cf + dict_obj[NameObject("/CF")] = cf + dict_obj[NameObject("/StmF")] = NameObject("/StdCF") + dict_obj[NameObject("/StrF")] = NameObject("/StdCF") + # ignore EFF + # dict_obj[NameObject("/EFF")] = NameObject("/StdCF") + + if self.V >= 5: + dict_obj[NameObject("/OE")] = ByteStringObject(self.values.OE) + dict_obj[NameObject("/UE")] = ByteStringObject(self.values.UE) + dict_obj[NameObject("/Perms")] = ByteStringObject(self.values.Perms) + return dict_obj + + def compute_values_v4(self, user_password: bytes, owner_password: bytes) -> None: + rc4_key = AlgV4.compute_O_value_key(owner_password, self.R, self.Length) + o_value = AlgV4.compute_O_value(rc4_key, user_password, self.R) + + key = AlgV4.compute_key( + user_password, + self.R, + self.Length, + o_value, + self.P, + self.id1_entry, + self.EncryptMetadata, + ) + u_value = AlgV4.compute_U_value(key, self.R, self.id1_entry) + + self._key = key + self.values.O = o_value + self.values.U = u_value + + @staticmethod + def read(encryption_entry: DictionaryObject, first_id_entry: bytes) -> "Encryption": + if encryption_entry.get("/Filter") != "/Standard": + raise NotImplementedError( + "only Standard PDF encryption handler is available" + ) + if "/SubFilter" in encryption_entry: + raise NotImplementedError("/SubFilter NOT supported") + + stm_filter = "/V2" + str_filter = "/V2" + ef_filter = "/V2" + + alg_ver = encryption_entry.get("/V", 0) + if alg_ver not in (1, 2, 3, 4, 5): + raise NotImplementedError(f"Encryption V={alg_ver} NOT supported") + if alg_ver >= 4: + filters = encryption_entry["/CF"] + + stm_filter = encryption_entry.get("/StmF", "/Identity") + str_filter = encryption_entry.get("/StrF", "/Identity") + ef_filter = encryption_entry.get("/EFF", stm_filter) + + if stm_filter != "/Identity": + stm_filter = filters[stm_filter]["/CFM"] # type: ignore + if str_filter != "/Identity": + str_filter = filters[str_filter]["/CFM"] # type: ignore + if ef_filter != "/Identity": + ef_filter = filters[ef_filter]["/CFM"] # type: ignore + + allowed_methods = ("/Identity", "/V2", "/AESV2", "/AESV3") + if stm_filter not in allowed_methods: + raise NotImplementedError(f"StmF Method {stm_filter} NOT supported!") + if str_filter not in allowed_methods: + raise NotImplementedError(f"StrF Method {str_filter} NOT supported!") + if ef_filter not in allowed_methods: + raise NotImplementedError(f"EFF Method {ef_filter} NOT supported!") + + alg_rev = cast(int, encryption_entry["/R"]) + perm_flags = cast(int, encryption_entry["/P"]) + key_bits = encryption_entry.get("/Length", 40) + encrypt_metadata = encryption_entry.get("/EncryptMetadata") + encrypt_metadata = ( + encrypt_metadata.value if encrypt_metadata is not None else True + ) + values = EncryptionValues() + values.O = cast(ByteStringObject, encryption_entry["/O"]).original_bytes + values.U = cast(ByteStringObject, encryption_entry["/U"]).original_bytes + values.OE = encryption_entry.get("/OE", ByteStringObject()).original_bytes + values.UE = encryption_entry.get("/UE", ByteStringObject()).original_bytes + values.Perms = encryption_entry.get("/Perms", ByteStringObject()).original_bytes + return Encryption( + V=alg_ver, + R=alg_rev, + Length=key_bits, + P=perm_flags, + EncryptMetadata=encrypt_metadata, + first_id_entry=first_id_entry, + values=values, + StrF=str_filter, + StmF=stm_filter, + EFF=ef_filter, + entry=encryption_entry, # Dummy entry for the moment; will get removed + ) + + @staticmethod + def make( + alg: EncryptAlgorithm, permissions: int, first_id_entry: bytes + ) -> "Encryption": + alg_ver, alg_rev, key_bits = alg + + stm_filter, str_filter, ef_filter = "/V2", "/V2", "/V2" + + if alg == EncryptAlgorithm.AES_128: + stm_filter, str_filter, ef_filter = "/AESV2", "/AESV2", "/AESV2" + elif alg in (EncryptAlgorithm.AES_256_R5, EncryptAlgorithm.AES_256): + stm_filter, str_filter, ef_filter = "/AESV3", "/AESV3", "/AESV3" + + return Encryption( + V=alg_ver, + R=alg_rev, + Length=key_bits, + P=permissions, + EncryptMetadata=True, + first_id_entry=first_id_entry, + values=None, + StrF=str_filter, + StmF=stm_filter, + EFF=ef_filter, + entry=DictionaryObject(), # Dummy entry for the moment; will get removed + ) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_merger.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_merger.py new file mode 100644 index 00000000..b6a83040 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_merger.py @@ -0,0 +1,42 @@ +# Copyright (c) 2006, Mathieu Fenniak +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +from ._utils import ( + deprecation_with_replacement, +) + + +class PdfMerger: + """ + Use :class:`PdfWriter` instead. + + .. deprecated:: 5.0.0 + """ + + def __init__(self) -> None: + deprecation_with_replacement("PdfMerger", "PdfWriter", "5.0.0") diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_page.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_page.py new file mode 100644 index 00000000..e4ec053c --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_page.py @@ -0,0 +1,2571 @@ +# Copyright (c) 2006, Mathieu Fenniak +# Copyright (c) 2007, Ashish Kulkarni +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import math +from dataclasses import dataclass +from decimal import Decimal +from io import BytesIO +from pathlib import Path +from typing import ( + Any, + Callable, + Dict, + Iterable, + Iterator, + List, + Literal, + Optional, + Sequence, + Set, + Tuple, + Union, + cast, + overload, +) + +from ._cmap import build_char_map, unknown_char_map +from ._protocols import PdfCommonDocProtocol +from ._text_extraction import ( + OrientationNotFoundError, + _layout_mode, + crlf_space_check, + handle_tj, + mult, +) +from ._utils import ( + CompressedTransformationMatrix, + TransformationMatrixType, + _human_readable_bytes, + logger_warning, + matrix_multiply, +) +from .constants import AnnotationDictionaryAttributes as ADA +from .constants import ImageAttributes as IA +from .constants import PageAttributes as PG +from .constants import Resources as RES +from .errors import PageSizeNotDefinedError, PdfReadError +from .filters import _xobj_to_image +from .generic import ( + ArrayObject, + ContentStream, + DictionaryObject, + EncodedStreamObject, + FloatObject, + IndirectObject, + NameObject, + NullObject, + NumberObject, + PdfObject, + RectangleObject, + StreamObject, + is_null_or_none, +) + +try: + from PIL.Image import Image + + pil_not_imported = False +except ImportError: + Image = object # type: ignore + pil_not_imported = True # error will be raised only when using images + +MERGE_CROP_BOX = "cropbox" # pypdf<=3.4.0 used 'trimbox' + + +def _get_rectangle(self: Any, name: str, defaults: Iterable[str]) -> RectangleObject: + retval: Union[None, RectangleObject, IndirectObject] = self.get(name) + if isinstance(retval, RectangleObject): + return retval + if is_null_or_none(retval): + for d in defaults: + retval = self.get(d) + if retval is not None: + break + if isinstance(retval, IndirectObject): + retval = self.pdf.get_object(retval) + retval = RectangleObject(retval) # type: ignore + _set_rectangle(self, name, retval) + return retval + + +def _set_rectangle(self: Any, name: str, value: Union[RectangleObject, float]) -> None: + name = NameObject(name) + self[name] = value + + +def _delete_rectangle(self: Any, name: str) -> None: + del self[name] + + +def _create_rectangle_accessor(name: str, fallback: Iterable[str]) -> property: + return property( + lambda self: _get_rectangle(self, name, fallback), + lambda self, value: _set_rectangle(self, name, value), + lambda self: _delete_rectangle(self, name), + ) + + +class Transformation: + """ + Represent a 2D transformation. + + The transformation between two coordinate systems is represented by a 3-by-3 + transformation matrix matrix with the following form:: + + a b 0 + c d 0 + e f 1 + + Because a transformation matrix has only six elements that can be changed, + it is usually specified in PDF as the six-element array [ a b c d e f ]. + + Coordinate transformations are expressed as matrix multiplications:: + + a b 0 + [ x′ y′ 1 ] = [ x y 1 ] × c d 0 + e f 1 + + + Example: + >>> from pypdf import Transformation + >>> op = Transformation().scale(sx=2, sy=3).translate(tx=10, ty=20) + >>> page.add_transformation(op) + """ + + # 9.5.4 Coordinate Systems for 3D + # 4.2.2 Common Transformations + def __init__(self, ctm: CompressedTransformationMatrix = (1, 0, 0, 1, 0, 0)): + self.ctm = ctm + + @property + def matrix(self) -> TransformationMatrixType: + """ + Return the transformation matrix as a tuple of tuples in the form: + + ((a, b, 0), (c, d, 0), (e, f, 1)) + """ + return ( + (self.ctm[0], self.ctm[1], 0), + (self.ctm[2], self.ctm[3], 0), + (self.ctm[4], self.ctm[5], 1), + ) + + @staticmethod + def compress(matrix: TransformationMatrixType) -> CompressedTransformationMatrix: + """ + Compresses the transformation matrix into a tuple of (a, b, c, d, e, f). + + Args: + matrix: The transformation matrix as a tuple of tuples. + + Returns: + A tuple representing the transformation matrix as (a, b, c, d, e, f) + """ + return ( + matrix[0][0], + matrix[0][1], + matrix[1][0], + matrix[1][1], + matrix[2][0], + matrix[2][1], + ) + + def transform(self, m: "Transformation") -> "Transformation": + """ + Apply one transformation to another. + + Args: + m: a Transformation to apply. + + Returns: + A new ``Transformation`` instance + + Example: + >>> from pypdf import Transformation + >>> op = Transformation((1, 0, 0, -1, 0, height)) # vertical mirror + >>> op = Transformation().transform(Transformation((-1, 0, 0, 1, iwidth, 0))) # horizontal mirror + >>> page.add_transformation(op) + """ + ctm = Transformation.compress(matrix_multiply(self.matrix, m.matrix)) + return Transformation(ctm) + + def translate(self, tx: float = 0, ty: float = 0) -> "Transformation": + """ + Translate the contents of a page. + + Args: + tx: The translation along the x-axis. + ty: The translation along the y-axis. + + Returns: + A new ``Transformation`` instance + """ + m = self.ctm + return Transformation(ctm=(m[0], m[1], m[2], m[3], m[4] + tx, m[5] + ty)) + + def scale( + self, sx: Optional[float] = None, sy: Optional[float] = None + ) -> "Transformation": + """ + Scale the contents of a page towards the origin of the coordinate system. + + Typically, that is the lower-left corner of the page. That can be + changed by translating the contents / the page boxes. + + Args: + sx: The scale factor along the x-axis. + sy: The scale factor along the y-axis. + + Returns: + A new Transformation instance with the scaled matrix. + """ + if sx is None and sy is None: + raise ValueError("Either sx or sy must be specified") + if sx is None: + sx = sy + if sy is None: + sy = sx + assert sx is not None + assert sy is not None + op: TransformationMatrixType = ((sx, 0, 0), (0, sy, 0), (0, 0, 1)) + ctm = Transformation.compress(matrix_multiply(self.matrix, op)) + return Transformation(ctm) + + def rotate(self, rotation: float) -> "Transformation": + """ + Rotate the contents of a page. + + Args: + rotation: The angle of rotation in degrees. + + Returns: + A new ``Transformation`` instance with the rotated matrix. + """ + rotation = math.radians(rotation) + op: TransformationMatrixType = ( + (math.cos(rotation), math.sin(rotation), 0), + (-math.sin(rotation), math.cos(rotation), 0), + (0, 0, 1), + ) + ctm = Transformation.compress(matrix_multiply(self.matrix, op)) + return Transformation(ctm) + + def __repr__(self) -> str: + return f"Transformation(ctm={self.ctm})" + + @overload + def apply_on(self, pt: List[float], as_object: bool = False) -> List[float]: + ... + + @overload + def apply_on( + self, pt: Tuple[float, float], as_object: bool = False + ) -> Tuple[float, float]: + ... + + def apply_on( + self, + pt: Union[Tuple[float, float], List[float]], + as_object: bool = False, + ) -> Union[Tuple[float, float], List[float]]: + """ + Apply the transformation matrix on the given point. + + Args: + pt: A tuple or list representing the point in the form (x, y) + + Returns: + A tuple or list representing the transformed point in the form (x', y') + """ + typ = FloatObject if as_object else float + pt1 = ( + typ(float(pt[0]) * self.ctm[0] + float(pt[1]) * self.ctm[2] + self.ctm[4]), + typ(float(pt[0]) * self.ctm[1] + float(pt[1]) * self.ctm[3] + self.ctm[5]), + ) + return list(pt1) if isinstance(pt, list) else pt1 + + +@dataclass +class ImageFile: + """ + Image within the PDF file. *This object is not designed to be built.* + + This object should not be modified except using :func:`ImageFile.replace` to replace the image with a new one. + """ + + name: str = "" + """ + Filename as identified within the PDF file. + """ + + data: bytes = b"" + """ + Data as bytes. + """ + + image: Optional[Image] = None + """ + Data as PIL image. + """ + + indirect_reference: Optional[IndirectObject] = None + """ + Reference to the object storing the stream. + """ + + def replace(self, new_image: Image, **kwargs: Any) -> None: + """ + Replace the image with a new PIL image. + + Args: + new_image (PIL.Image.Image): The new PIL image to replace the existing image. + **kwargs: Additional keyword arguments to pass to `Image.save()`. + + Raises: + TypeError: If the image is inline or in a PdfReader. + TypeError: If the image does not belong to a PdfWriter. + TypeError: If `new_image` is not a PIL Image. + + Note: + This method replaces the existing image with a new image. + It is not allowed for inline images or images within a PdfReader. + The `kwargs` parameter allows passing additional parameters + to `Image.save()`, such as quality. + """ + if pil_not_imported: + raise ImportError( + "pillow is required to do image extraction. " + "It can be installed via 'pip install pypdf[image]'" + ) + + from ._reader import PdfReader + + # to prevent circular import + from .filters import _xobj_to_image + from .generic import DictionaryObject, PdfObject + + if self.indirect_reference is None: + raise TypeError("Cannot update an inline image.") + if not hasattr(self.indirect_reference.pdf, "_id_translated"): + raise TypeError("Cannot update an image not belonging to a PdfWriter.") + if not isinstance(new_image, Image): + raise TypeError("new_image shall be a PIL Image") + b = BytesIO() + new_image.save(b, "PDF", **kwargs) + reader = PdfReader(b) + assert reader.pages[0].images[0].indirect_reference is not None + self.indirect_reference.pdf._objects[self.indirect_reference.idnum - 1] = ( + reader.pages[0].images[0].indirect_reference.get_object() + ) + cast( + PdfObject, self.indirect_reference.get_object() + ).indirect_reference = self.indirect_reference + # change the object attributes + extension, byte_stream, img = _xobj_to_image( + cast(DictionaryObject, self.indirect_reference.get_object()) + ) + assert extension is not None + self.name = self.name[: self.name.rfind(".")] + extension + self.data = byte_stream + self.image = img + + def __str__(self) -> str: + return f"{self.__class__.__name__}(name={self.name}, data: {_human_readable_bytes(len(self.data))})" + + def __repr__(self) -> str: + return self.__str__()[:-1] + f", hash: {hash(self.data)})" + + +class VirtualListImages(Sequence[ImageFile]): + """ + Provides access to images referenced within a page. + Only one copy will be returned if the usage is used on the same page multiple times. + See :func:`PageObject.images` for more details. + """ + + def __init__( + self, + ids_function: Callable[[], List[Union[str, List[str]]]], + get_function: Callable[[Union[str, List[str], Tuple[str]]], ImageFile], + ) -> None: + self.ids_function = ids_function + self.get_function = get_function + self.current = -1 + + def __len__(self) -> int: + return len(self.ids_function()) + + def keys(self) -> List[Union[str, List[str]]]: + return self.ids_function() + + def items(self) -> List[Tuple[Union[str, List[str]], ImageFile]]: + return [(x, self[x]) for x in self.ids_function()] + + @overload + def __getitem__(self, index: Union[int, str, List[str]]) -> ImageFile: + ... + + @overload + def __getitem__(self, index: slice) -> Sequence[ImageFile]: + ... + + def __getitem__( + self, index: Union[int, slice, str, List[str], Tuple[str]] + ) -> Union[ImageFile, Sequence[ImageFile]]: + lst = self.ids_function() + if isinstance(index, slice): + indices = range(*index.indices(len(self))) + lst = [lst[x] for x in indices] + cls = type(self) + return cls((lambda: lst), self.get_function) + if isinstance(index, (str, list, tuple)): + return self.get_function(index) + if not isinstance(index, int): + raise TypeError("invalid sequence indices type") + len_self = len(lst) + if index < 0: + # support negative indexes + index = len_self + index + if index < 0 or index >= len_self: + raise IndexError("sequence index out of range") + return self.get_function(lst[index]) + + def __iter__(self) -> Iterator[ImageFile]: + for i in range(len(self)): + yield self[i] + + def __str__(self) -> str: + p = [f"Image_{i}={n}" for i, n in enumerate(self.ids_function())] + return f"[{', '.join(p)}]" + + +class PageObject(DictionaryObject): + """ + PageObject represents a single page within a PDF file. + + Typically these objects will be created by accessing the + :attr:`pages` property of the + :class:`PdfReader` class, but it is + also possible to create an empty page with the + :meth:`create_blank_page()` static method. + + Args: + pdf: PDF file the page belongs to. + indirect_reference: Stores the original indirect reference to + this object in its source PDF + """ + + original_page: "PageObject" # very local use in writer when appending + + def __init__( + self, + pdf: Optional[PdfCommonDocProtocol] = None, + indirect_reference: Optional[IndirectObject] = None, + ) -> None: + DictionaryObject.__init__(self) + self.pdf = pdf + self.inline_images: Optional[Dict[str, ImageFile]] = None + # below Union for mypy but actually Optional[List[str]] + self.indirect_reference = indirect_reference + if not is_null_or_none(indirect_reference): + assert indirect_reference is not None, "mypy" + self.update(cast(DictionaryObject, indirect_reference.get_object())) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Note: this function is overloaded to return the same results + as a DictionaryObject. + + Returns: + Hash considering type and value. + """ + return hash( + (DictionaryObject, tuple(((k, v.hash_bin()) for k, v in self.items()))) + ) + + def hash_value_data(self) -> bytes: + data = super().hash_value_data() + data += b"%d" % id(self) + return data + + @property + def user_unit(self) -> float: + """ + A read-only positive number giving the size of user space units. + + It is in multiples of 1/72 inch. Hence a value of 1 means a user + space unit is 1/72 inch, and a value of 3 means that a user + space unit is 3/72 inch. + """ + return self.get(PG.USER_UNIT, 1) + + @staticmethod + def create_blank_page( + pdf: Optional[PdfCommonDocProtocol] = None, + width: Union[float, Decimal, None] = None, + height: Union[float, Decimal, None] = None, + ) -> "PageObject": + """ + Return a new blank page. + + If ``width`` or ``height`` is ``None``, try to get the page size + from the last page of *pdf*. + + Args: + pdf: PDF file the page is within. + width: The width of the new page expressed in default user + space units. + height: The height of the new page expressed in default user + space units. + + Returns: + The new blank page + + Raises: + PageSizeNotDefinedError: if ``pdf`` is ``None`` or contains + no page + """ + page = PageObject(pdf) + + # Creates a new page (cf PDF Reference 7.7.3.3) + page.__setitem__(NameObject(PG.TYPE), NameObject("/Page")) + page.__setitem__(NameObject(PG.PARENT), NullObject()) + page.__setitem__(NameObject(PG.RESOURCES), DictionaryObject()) + if width is None or height is None: + if pdf is not None and len(pdf.pages) > 0: + lastpage = pdf.pages[len(pdf.pages) - 1] + width = lastpage.mediabox.width + height = lastpage.mediabox.height + else: + raise PageSizeNotDefinedError + page.__setitem__( + NameObject(PG.MEDIABOX), RectangleObject((0, 0, width, height)) # type: ignore + ) + + return page + + def _get_ids_image( + self, + obj: Optional[DictionaryObject] = None, + ancest: Optional[List[str]] = None, + call_stack: Optional[List[Any]] = None, + ) -> List[Union[str, List[str]]]: + if call_stack is None: + call_stack = [] + _i = getattr(obj, "indirect_reference", None) + if _i in call_stack: + return [] + else: + call_stack.append(_i) + if self.inline_images is None: + self.inline_images = self._get_inline_images() + if obj is None: + obj = self + if ancest is None: + ancest = [] + lst: List[Union[str, List[str]]] = [] + if PG.RESOURCES not in obj or RES.XOBJECT not in cast( + DictionaryObject, obj[PG.RESOURCES] + ): + return [] if self.inline_images is None else list(self.inline_images.keys()) + + x_object = obj[PG.RESOURCES][RES.XOBJECT].get_object() # type: ignore + for o in x_object: + if not isinstance(x_object[o], StreamObject): + continue + if x_object[o][IA.SUBTYPE] == "/Image": + lst.append(o if len(ancest) == 0 else ancest + [o]) + else: # is a form with possible images inside + lst.extend(self._get_ids_image(x_object[o], ancest + [o], call_stack)) + assert self.inline_images is not None + lst.extend(list(self.inline_images.keys())) + return lst + + def _get_image( + self, + id: Union[str, List[str], Tuple[str]], + obj: Optional[DictionaryObject] = None, + ) -> ImageFile: + if obj is None: + obj = cast(DictionaryObject, self) + if isinstance(id, tuple): + id = list(id) + if isinstance(id, List) and len(id) == 1: + id = id[0] + try: + xobjs = cast( + DictionaryObject, cast(DictionaryObject, obj[PG.RESOURCES])[RES.XOBJECT] + ) + except KeyError: + if not (id[0] == "~" and id[-1] == "~"): + raise + if isinstance(id, str): + if id[0] == "~" and id[-1] == "~": + if self.inline_images is None: + self.inline_images = self._get_inline_images() + if self.inline_images is None: # pragma: no cover + raise KeyError("no inline image can be found") + return self.inline_images[id] + + imgd = _xobj_to_image(cast(DictionaryObject, xobjs[id])) + extension, byte_stream = imgd[:2] + f = ImageFile( + name=f"{id[1:]}{extension}", + data=byte_stream, + image=imgd[2], + indirect_reference=xobjs[id].indirect_reference, + ) + return f + else: # in a sub object + ids = id[1:] + return self._get_image(ids, cast(DictionaryObject, xobjs[id[0]])) + + @property + def images(self) -> VirtualListImages: + """ + Read-only property emulating a list of images on a page. + + Get a list of all images on the page. The key can be: + - A string (for the top object) + - A tuple (for images within XObject forms) + - An integer + + Examples: + * `reader.pages[0].images[0]` # return fist image + * `reader.pages[0].images['/I0']` # return image '/I0' + * `reader.pages[0].images['/TP1','/Image1']` # return image '/Image1' within '/TP1' Xobject/Form + * `for img in reader.pages[0].images:` # loops through all objects + + images.keys() and images.items() can be used. + + The ImageFile has the following properties: + + * `.name` : name of the object + * `.data` : bytes of the object + * `.image` : PIL Image Object + * `.indirect_reference` : object reference + + and the following methods: + `.replace(new_image: PIL.Image.Image, **kwargs)` : + replace the image in the pdf with the new image + applying the saving parameters indicated (such as quality) + + Example usage: + + reader.pages[0].images[0]=replace(Image.open("new_image.jpg", quality = 20) + + Inline images are extracted and named ~0~, ~1~, ..., with the + indirect_reference set to None. + """ + return VirtualListImages(self._get_ids_image, self._get_image) + + def _translate_value_inlineimage(self, k: str, v: PdfObject) -> PdfObject: + """Translate values used in inline image""" + try: + v = NameObject( + { + "/G": "/DeviceGray", + "/RGB": "/DeviceRGB", + "/CMYK": "/DeviceCMYK", + "/I": "/Indexed", + "/AHx": "/ASCIIHexDecode", + "/A85": "/ASCII85Decode", + "/LZW": "/LZWDecode", + "/Fl": "/FlateDecode", + "/RL": "/RunLengthDecode", + "/CCF": "/CCITTFaxDecode", + "/DCT": "/DCTDecode", + "/DeviceGray": "/DeviceGray", + "/DeviceRGB": "/DeviceRGB", + "/DeviceCMYK": "/DeviceCMYK", + "/Indexed": "/Indexed", + "/ASCIIHexDecode": "/ASCIIHexDecode", + "/ASCII85Decode": "/ASCII85Decode", + "/LZWDecode": "/LZWDecode", + "/FlateDecode": "/FlateDecode", + "/RunLengthDecode": "/RunLengthDecode", + "/CCITTFaxDecode": "/CCITTFaxDecode", + "/DCTDecode": "/DCTDecode", + }[cast(str, v)] + ) + except (TypeError, KeyError): + if isinstance(v, NameObject): + # It is a custom name, thus we have to look in resources. + # The only applicable case is for ColorSpace. + try: + res = cast(DictionaryObject, self["/Resources"])["/ColorSpace"] + v = cast(DictionaryObject, res)[v] + except KeyError: # for res and v + raise PdfReadError(f"Cannot find resource entry {v} for {k}") + return v + + def _get_inline_images(self) -> Dict[str, ImageFile]: + """ + get inline_images + entries will be identified as ~1~ + """ + content = self.get_contents() + if is_null_or_none(content): + return {} + imgs_data = [] + assert content is not None, "mypy" + for param, ope in content.operations: + if ope == b"INLINE IMAGE": + imgs_data.append( + {"settings": param["settings"], "__streamdata__": param["data"]} + ) + elif ope in (b"BI", b"EI", b"ID"): # pragma: no cover + raise PdfReadError( + f"{ope} operator met whereas not expected," + "please share usecase with pypdf dev team" + ) + """backup + elif ope == b"BI": + img_data["settings"] = {} + elif ope == b"EI": + imgs_data.append(img_data) + img_data = {} + elif ope == b"ID": + img_data["__streamdata__"] = b"" + elif "__streamdata__" in img_data: + if len(img_data["__streamdata__"]) > 0: + img_data["__streamdata__"] += b"\n" + raise Exception("check append") + img_data["__streamdata__"] += param + elif "settings" in img_data: + img_data["settings"][ope.decode()] = param + """ + files = {} + for num, ii in enumerate(imgs_data): + init = { + "__streamdata__": ii["__streamdata__"], + "/Length": len(ii["__streamdata__"]), + } + for k, v in ii["settings"].items(): + if k in {"/Length", "/L"}: # no length is expected + continue + if isinstance(v, list): + v = ArrayObject( + [self._translate_value_inlineimage(k, x) for x in v] + ) + else: + v = self._translate_value_inlineimage(k, v) + k = NameObject( + { + "/BPC": "/BitsPerComponent", + "/CS": "/ColorSpace", + "/D": "/Decode", + "/DP": "/DecodeParms", + "/F": "/Filter", + "/H": "/Height", + "/W": "/Width", + "/I": "/Interpolate", + "/Intent": "/Intent", + "/IM": "/ImageMask", + "/BitsPerComponent": "/BitsPerComponent", + "/ColorSpace": "/ColorSpace", + "/Decode": "/Decode", + "/DecodeParms": "/DecodeParms", + "/Filter": "/Filter", + "/Height": "/Height", + "/Width": "/Width", + "/Interpolate": "/Interpolate", + "/ImageMask": "/ImageMask", + }[k] + ) + if k not in init: + init[k] = v + ii["object"] = EncodedStreamObject.initialize_from_dictionary(init) + extension, byte_stream, img = _xobj_to_image(ii["object"]) + files[f"~{num}~"] = ImageFile( + name=f"~{num}~{extension}", + data=byte_stream, + image=img, + indirect_reference=None, + ) + return files + + @property + def rotation(self) -> int: + """ + The visual rotation of the page. + + This number has to be a multiple of 90 degrees: 0, 90, 180, or 270 are + valid values. This property does not affect ``/Contents``. + """ + rotate_obj = self.get(PG.ROTATE, 0) + return rotate_obj if isinstance(rotate_obj, int) else rotate_obj.get_object() + + @rotation.setter + def rotation(self, r: float) -> None: + self[NameObject(PG.ROTATE)] = NumberObject((((int(r) + 45) // 90) * 90) % 360) + + def transfer_rotation_to_content(self) -> None: + """ + Apply the rotation of the page to the content and the media/crop/... + boxes. + + It is recommended to apply this function before page merging. + """ + r = -self.rotation # rotation to apply is in the otherway + self.rotation = 0 + mb = RectangleObject(self.mediabox) + trsf = ( + Transformation() + .translate( + -float(mb.left + mb.width / 2), -float(mb.bottom + mb.height / 2) + ) + .rotate(r) + ) + pt1 = trsf.apply_on(mb.lower_left) + pt2 = trsf.apply_on(mb.upper_right) + trsf = trsf.translate(-min(pt1[0], pt2[0]), -min(pt1[1], pt2[1])) + self.add_transformation(trsf, False) + for b in ["/MediaBox", "/CropBox", "/BleedBox", "/TrimBox", "/ArtBox"]: + if b in self: + rr = RectangleObject(self[b]) # type: ignore + pt1 = trsf.apply_on(rr.lower_left) + pt2 = trsf.apply_on(rr.upper_right) + self[NameObject(b)] = RectangleObject( + ( + min(pt1[0], pt2[0]), + min(pt1[1], pt2[1]), + max(pt1[0], pt2[0]), + max(pt1[1], pt2[1]), + ) + ) + + def rotate(self, angle: int) -> "PageObject": + """ + Rotate a page clockwise by increments of 90 degrees. + + Args: + angle: Angle to rotate the page. Must be an increment of 90 deg. + + Returns: + The rotated PageObject + """ + if angle % 90 != 0: + raise ValueError("Rotation angle must be a multiple of 90") + self[NameObject(PG.ROTATE)] = NumberObject(self.rotation + angle) + return self + + def _merge_resources( + self, + res1: DictionaryObject, + res2: DictionaryObject, + resource: Any, + new_res1: bool = True, + ) -> Tuple[Dict[str, Any], Dict[str, Any]]: + try: + assert isinstance(self.indirect_reference, IndirectObject) + pdf = self.indirect_reference.pdf + is_pdf_writer = hasattr( + pdf, "_add_object" + ) # ---------- expect isinstance(pdf,PdfWriter) + except (AssertionError, AttributeError): + pdf = None + is_pdf_writer = False + + def compute_unique_key(base_key: str) -> Tuple[str, bool]: + """ + Find a key that either doesn't already exist or has the same value + (indicated by the bool) + + Args: + base_key: An index is added to this to get the computed key + + Returns: + A tuple (computed key, bool) where the boolean indicates + if there is a resource of the given computed_key with the same + value. + """ + value = page2res.raw_get(base_key) + # TODO : possible improvement : in case of writer, the indirect_reference + # can not be found because translated : this may be improved + + # try the current key first (e.g. "foo"), but otherwise iterate + # through "foo-0", "foo-1", etc. new_res can contain only finitely + # many keys, thus this'll eventually end, even if it's been crafted + # to be maximally annoying. + computed_key = base_key + idx = 0 + while computed_key in new_res: + if new_res.raw_get(computed_key) == value: + # there's already a resource of this name, with the exact + # same value + return computed_key, True + computed_key = f"{base_key}-{idx}" + idx += 1 + return computed_key, False + + if new_res1: + new_res = DictionaryObject() + new_res.update(res1.get(resource, DictionaryObject()).get_object()) + else: + new_res = cast(DictionaryObject, res1[resource]) + page2res = cast( + DictionaryObject, res2.get(resource, DictionaryObject()).get_object() + ) + rename_res = {} + for key in page2res: + unique_key, same_value = compute_unique_key(key) + newname = NameObject(unique_key) + if key != unique_key: + # we have to use a different name for this + rename_res[key] = newname + + if not same_value: + if is_pdf_writer: + new_res[newname] = page2res.raw_get(key).clone(pdf) + try: + new_res[newname] = new_res[newname].indirect_reference + except AttributeError: + pass + else: + new_res[newname] = page2res.raw_get(key) + lst = sorted(new_res.items()) + new_res.clear() + for el in lst: + new_res[el[0]] = el[1] + return new_res, rename_res + + @staticmethod + def _content_stream_rename( + stream: ContentStream, + rename: Dict[Any, Any], + pdf: Optional[PdfCommonDocProtocol], + ) -> ContentStream: + if not rename: + return stream + stream = ContentStream(stream, pdf) + for operands, _operator in stream.operations: + if isinstance(operands, list): + for i, op in enumerate(operands): + if isinstance(op, NameObject): + operands[i] = rename.get(op, op) + elif isinstance(operands, dict): + for i, op in operands.items(): + if isinstance(op, NameObject): + operands[i] = rename.get(op, op) + else: + raise KeyError(f"type of operands is {type(operands)}") + return stream + + @staticmethod + def _add_transformation_matrix( + contents: Any, + pdf: Optional[PdfCommonDocProtocol], + ctm: CompressedTransformationMatrix, + ) -> ContentStream: + """Add transformation matrix at the beginning of the given contents stream.""" + a, b, c, d, e, f = ctm + contents = ContentStream(contents, pdf) + contents.operations.insert( + 0, + [ + [ + FloatObject(a), + FloatObject(b), + FloatObject(c), + FloatObject(d), + FloatObject(e), + FloatObject(f), + ], + b"cm", + ], + ) + return contents + + def _get_contents_as_bytes(self) -> Optional[bytes]: + """ + Return the page contents as bytes. + + Returns: + The ``/Contents`` object as bytes, or ``None`` if it doesn't exist. + + """ + if PG.CONTENTS in self: + obj = self[PG.CONTENTS].get_object() + if isinstance(obj, list): + return b"".join(x.get_object().get_data() for x in obj) + else: + return cast(EncodedStreamObject, obj).get_data() + else: + return None + + def get_contents(self) -> Optional[ContentStream]: + """ + Access the page contents. + + Returns: + The ``/Contents`` object, or ``None`` if it does not exist. + ``/Contents`` is optional, as described in §7.7.3.3 of the PDF Reference. + """ + if PG.CONTENTS in self: + try: + pdf = cast(IndirectObject, self.indirect_reference).pdf + except AttributeError: + pdf = None + obj = self[PG.CONTENTS].get_object() + if isinstance(obj, NullObject): + return None + else: + return ContentStream(obj, pdf) + else: + return None + + def replace_contents( + self, content: Union[None, ContentStream, EncodedStreamObject, ArrayObject] + ) -> None: + """ + Replace the page contents with the new content and nullify old objects + Args: + content: new content; if None delete the content field. + """ + if not hasattr(self, "indirect_reference") or self.indirect_reference is None: + # the page is not attached : the content is directly attached. + self[NameObject(PG.CONTENTS)] = content + return + if isinstance(self.get(PG.CONTENTS, None), ArrayObject): + for o in self[PG.CONTENTS]: # type: ignore[attr-defined] + try: + self._objects[o.indirect_reference.idnum - 1] = NullObject() # type: ignore + except AttributeError: + pass + + if isinstance(content, ArrayObject): + for i in range(len(content)): + content[i] = self.indirect_reference.pdf._add_object(content[i]) + + if is_null_or_none(content): + if PG.CONTENTS not in self: + return + else: + assert self.indirect_reference is not None + assert self[PG.CONTENTS].indirect_reference is not None + self.indirect_reference.pdf._objects[ + self[PG.CONTENTS].indirect_reference.idnum - 1 # type: ignore + ] = NullObject() + del self[PG.CONTENTS] + elif not hasattr(self.get(PG.CONTENTS, None), "indirect_reference"): + try: + self[NameObject(PG.CONTENTS)] = self.indirect_reference.pdf._add_object( + content + ) + except AttributeError: + # applies at least for page not in writer + # as a backup solution, we put content as an object although not in accordance with pdf ref + # this will be fixed with the _add_object + self[NameObject(PG.CONTENTS)] = content + else: + assert content is not None, "mypy" + content.indirect_reference = self[ + PG.CONTENTS + ].indirect_reference # TODO: in a future may required generation management + try: + self.indirect_reference.pdf._objects[ + content.indirect_reference.idnum - 1 # type: ignore + ] = content + except AttributeError: + # applies at least for page not in writer + # as a backup solution, we put content as an object although not in accordance with pdf ref + # this will be fixed with the _add_object + self[NameObject(PG.CONTENTS)] = content + # forces recalculation of inline_images + self.inline_images = None + + def merge_page( + self, page2: "PageObject", expand: bool = False, over: bool = True + ) -> None: + """ + Merge the content streams of two pages into one. + + Resource references + (i.e. fonts) are maintained from both pages. The mediabox/cropbox/etc + of this page are not altered. The parameter page's content stream will + be added to the end of this page's content stream, meaning that it will + be drawn after, or "on top" of this page. + + Args: + page2: The page to be merged into this one. Should be + an instance of :class:`PageObject`. + over: set the page2 content over page1 if True (default) else under + expand: If True, the current page dimensions will be + expanded to accommodate the dimensions of the page to be merged. + """ + self._merge_page(page2, over=over, expand=expand) + + def _merge_page( + self, + page2: "PageObject", + page2transformation: Optional[Callable[[Any], ContentStream]] = None, + ctm: Optional[CompressedTransformationMatrix] = None, + over: bool = True, + expand: bool = False, + ) -> None: + # First we work on merging the resource dictionaries. This allows us + # to find out what symbols in the content streams we might need to + # rename. + try: + assert isinstance(self.indirect_reference, IndirectObject) + if hasattr( + self.indirect_reference.pdf, "_add_object" + ): # ---------- to detect PdfWriter + return self._merge_page_writer( + page2, page2transformation, ctm, over, expand + ) + except (AssertionError, AttributeError): + pass + + new_resources = DictionaryObject() + rename = {} + try: + original_resources = cast(DictionaryObject, self[PG.RESOURCES].get_object()) + except KeyError: + original_resources = DictionaryObject() + try: + page2resources = cast(DictionaryObject, page2[PG.RESOURCES].get_object()) + except KeyError: + page2resources = DictionaryObject() + new_annots = ArrayObject() + + for page in (self, page2): + if PG.ANNOTS in page: + annots = page[PG.ANNOTS] + if isinstance(annots, ArrayObject): + new_annots.extend(annots) + + for res in ( + RES.EXT_G_STATE, + RES.FONT, + RES.XOBJECT, + RES.COLOR_SPACE, + RES.PATTERN, + RES.SHADING, + RES.PROPERTIES, + ): + new, newrename = self._merge_resources( + original_resources, page2resources, res + ) + if new: + new_resources[NameObject(res)] = new + rename.update(newrename) + + # Combine /ProcSet sets, making sure there's a consistent order + new_resources[NameObject(RES.PROC_SET)] = ArrayObject( + sorted( + set( + original_resources.get(RES.PROC_SET, ArrayObject()).get_object() + ).union( + set(page2resources.get(RES.PROC_SET, ArrayObject()).get_object()) + ) + ) + ) + + new_content_array = ArrayObject() + original_content = self.get_contents() + if original_content is not None: + original_content.isolate_graphics_state() + new_content_array.append(original_content) + + page2content = page2.get_contents() + if page2content is not None: + rect = getattr(page2, MERGE_CROP_BOX) + page2content.operations.insert( + 0, + ( + map( + FloatObject, + [ + rect.left, + rect.bottom, + rect.width, + rect.height, + ], + ), + b"re", + ), + ) + page2content.operations.insert(1, ([], b"W")) + page2content.operations.insert(2, ([], b"n")) + if page2transformation is not None: + page2content = page2transformation(page2content) + page2content = PageObject._content_stream_rename( + page2content, rename, self.pdf + ) + page2content.isolate_graphics_state() + if over: + new_content_array.append(page2content) + else: + new_content_array.insert(0, page2content) + + # if expanding the page to fit a new page, calculate the new media box size + if expand: + self._expand_mediabox(page2, ctm) + + self.replace_contents(ContentStream(new_content_array, self.pdf)) + self[NameObject(PG.RESOURCES)] = new_resources + self[NameObject(PG.ANNOTS)] = new_annots + + def _merge_page_writer( + self, + page2: "PageObject", + page2transformation: Optional[Callable[[Any], ContentStream]] = None, + ctm: Optional[CompressedTransformationMatrix] = None, + over: bool = True, + expand: bool = False, + ) -> None: + # First we work on merging the resource dictionaries. This allows us + # to find which symbols in the content streams we might need to + # rename. + assert isinstance(self.indirect_reference, IndirectObject) + pdf = self.indirect_reference.pdf + + rename = {} + if PG.RESOURCES not in self: + self[NameObject(PG.RESOURCES)] = DictionaryObject() + original_resources = cast(DictionaryObject, self[PG.RESOURCES].get_object()) + if PG.RESOURCES not in page2: + page2resources = DictionaryObject() + else: + page2resources = cast(DictionaryObject, page2[PG.RESOURCES].get_object()) + + for res in ( + RES.EXT_G_STATE, + RES.FONT, + RES.XOBJECT, + RES.COLOR_SPACE, + RES.PATTERN, + RES.SHADING, + RES.PROPERTIES, + ): + if res in page2resources: + if res not in original_resources: + original_resources[NameObject(res)] = DictionaryObject() + _, newrename = self._merge_resources( + original_resources, page2resources, res, False + ) + rename.update(newrename) + # Combine /ProcSet sets. + if RES.PROC_SET in page2resources: + if RES.PROC_SET not in original_resources: + original_resources[NameObject(RES.PROC_SET)] = ArrayObject() + arr = cast(ArrayObject, original_resources[RES.PROC_SET]) + for x in cast(ArrayObject, page2resources[RES.PROC_SET]): + if x not in arr: + arr.append(x) + arr.sort() + + if PG.ANNOTS in page2: + if PG.ANNOTS not in self: + self[NameObject(PG.ANNOTS)] = ArrayObject() + annots = cast(ArrayObject, self[PG.ANNOTS].get_object()) + if ctm is None: + trsf = Transformation() + else: + trsf = Transformation(ctm) + for a in cast(ArrayObject, page2[PG.ANNOTS]): + a = a.get_object() + aa = a.clone( + pdf, + ignore_fields=("/P", "/StructParent", "/Parent"), + force_duplicate=True, + ) + r = cast(ArrayObject, a["/Rect"]) + pt1 = trsf.apply_on((r[0], r[1]), True) + pt2 = trsf.apply_on((r[2], r[3]), True) + aa[NameObject("/Rect")] = ArrayObject( + ( + min(pt1[0], pt2[0]), + min(pt1[1], pt2[1]), + max(pt1[0], pt2[0]), + max(pt1[1], pt2[1]), + ) + ) + if "/QuadPoints" in a: + q = cast(ArrayObject, a["/QuadPoints"]) + aa[NameObject("/QuadPoints")] = ArrayObject( + trsf.apply_on((q[0], q[1]), True) + + trsf.apply_on((q[2], q[3]), True) + + trsf.apply_on((q[4], q[5]), True) + + trsf.apply_on((q[6], q[7]), True) + ) + try: + aa["/Popup"][NameObject("/Parent")] = aa.indirect_reference + except KeyError: + pass + try: + aa[NameObject("/P")] = self.indirect_reference + annots.append(aa.indirect_reference) + except AttributeError: + pass + + new_content_array = ArrayObject() + original_content = self.get_contents() + if original_content is not None: + original_content.isolate_graphics_state() + new_content_array.append(original_content) + + page2content = page2.get_contents() + if page2content is not None: + rect = getattr(page2, MERGE_CROP_BOX) + page2content.operations.insert( + 0, + ( + map( + FloatObject, + [ + rect.left, + rect.bottom, + rect.width, + rect.height, + ], + ), + b"re", + ), + ) + page2content.operations.insert(1, ([], b"W")) + page2content.operations.insert(2, ([], b"n")) + if page2transformation is not None: + page2content = page2transformation(page2content) + page2content = PageObject._content_stream_rename( + page2content, rename, self.pdf + ) + page2content.isolate_graphics_state() + if over: + new_content_array.append(page2content) + else: + new_content_array.insert(0, page2content) + + # if expanding the page to fit a new page, calculate the new media box size + if expand: + self._expand_mediabox(page2, ctm) + + self.replace_contents(new_content_array) + # self[NameObject(PG.CONTENTS)] = ContentStream(new_content_array, pdf) + # self[NameObject(PG.RESOURCES)] = new_resources + # self[NameObject(PG.ANNOTS)] = new_annots + + def _expand_mediabox( + self, page2: "PageObject", ctm: Optional[CompressedTransformationMatrix] + ) -> None: + corners1 = ( + self.mediabox.left.as_numeric(), + self.mediabox.bottom.as_numeric(), + self.mediabox.right.as_numeric(), + self.mediabox.top.as_numeric(), + ) + corners2 = ( + page2.mediabox.left.as_numeric(), + page2.mediabox.bottom.as_numeric(), + page2.mediabox.left.as_numeric(), + page2.mediabox.top.as_numeric(), + page2.mediabox.right.as_numeric(), + page2.mediabox.top.as_numeric(), + page2.mediabox.right.as_numeric(), + page2.mediabox.bottom.as_numeric(), + ) + if ctm is not None: + ctm = tuple(float(x) for x in ctm) # type: ignore[assignment] + new_x = tuple( + ctm[0] * corners2[i] + ctm[2] * corners2[i + 1] + ctm[4] + for i in range(0, 8, 2) + ) + new_y = tuple( + ctm[1] * corners2[i] + ctm[3] * corners2[i + 1] + ctm[5] + for i in range(0, 8, 2) + ) + else: + new_x = corners2[0:8:2] + new_y = corners2[1:8:2] + lowerleft = (min(new_x), min(new_y)) + upperright = (max(new_x), max(new_y)) + lowerleft = (min(corners1[0], lowerleft[0]), min(corners1[1], lowerleft[1])) + upperright = ( + max(corners1[2], upperright[0]), + max(corners1[3], upperright[1]), + ) + + self.mediabox.lower_left = lowerleft + self.mediabox.upper_right = upperright + + def merge_transformed_page( + self, + page2: "PageObject", + ctm: Union[CompressedTransformationMatrix, Transformation], + over: bool = True, + expand: bool = False, + ) -> None: + """ + merge_transformed_page is similar to merge_page, but a transformation + matrix is applied to the merged stream. + + Args: + page2: The page to be merged into this one. + ctm: a 6-element tuple containing the operands of the + transformation matrix + over: set the page2 content over page1 if True (default) else under + expand: Whether the page should be expanded to fit the dimensions + of the page to be merged. + """ + if isinstance(ctm, Transformation): + ctm = ctm.ctm + self._merge_page( + page2, + lambda page2Content: PageObject._add_transformation_matrix( + page2Content, page2.pdf, cast(CompressedTransformationMatrix, ctm) + ), + ctm, + over, + expand, + ) + + def merge_scaled_page( + self, page2: "PageObject", scale: float, over: bool = True, expand: bool = False + ) -> None: + """ + merge_scaled_page is similar to merge_page, but the stream to be merged + is scaled by applying a transformation matrix. + + Args: + page2: The page to be merged into this one. + scale: The scaling factor + over: set the page2 content over page1 if True (default) else under + expand: Whether the page should be expanded to fit the + dimensions of the page to be merged. + """ + op = Transformation().scale(scale, scale) + self.merge_transformed_page(page2, op, over, expand) + + def merge_rotated_page( + self, + page2: "PageObject", + rotation: float, + over: bool = True, + expand: bool = False, + ) -> None: + """ + merge_rotated_page is similar to merge_page, but the stream to be merged + is rotated by applying a transformation matrix. + + Args: + page2: The page to be merged into this one. + rotation: The angle of the rotation, in degrees + over: set the page2 content over page1 if True (default) else under + expand: Whether the page should be expanded to fit the + dimensions of the page to be merged. + """ + op = Transformation().rotate(rotation) + self.merge_transformed_page(page2, op, over, expand) + + def merge_translated_page( + self, + page2: "PageObject", + tx: float, + ty: float, + over: bool = True, + expand: bool = False, + ) -> None: + """ + mergeTranslatedPage is similar to merge_page, but the stream to be + merged is translated by applying a transformation matrix. + + Args: + page2: the page to be merged into this one. + tx: The translation on X axis + ty: The translation on Y axis + over: set the page2 content over page1 if True (default) else under + expand: Whether the page should be expanded to fit the + dimensions of the page to be merged. + """ + op = Transformation().translate(tx, ty) + self.merge_transformed_page(page2, op, over, expand) + + def add_transformation( + self, + ctm: Union[Transformation, CompressedTransformationMatrix], + expand: bool = False, + ) -> None: + """ + Apply a transformation matrix to the page. + + Args: + ctm: A 6-element tuple containing the operands of the + transformation matrix. Alternatively, a + :py:class:`Transformation` + object can be passed. + + See :doc:`/user/cropping-and-transforming`. + """ + if isinstance(ctm, Transformation): + ctm = ctm.ctm + content = self.get_contents() + if content is not None: + content = PageObject._add_transformation_matrix(content, self.pdf, ctm) + content.isolate_graphics_state() + self.replace_contents(content) + # if expanding the page to fit a new page, calculate the new media box size + if expand: + corners = [ + self.mediabox.left.as_numeric(), + self.mediabox.bottom.as_numeric(), + self.mediabox.left.as_numeric(), + self.mediabox.top.as_numeric(), + self.mediabox.right.as_numeric(), + self.mediabox.top.as_numeric(), + self.mediabox.right.as_numeric(), + self.mediabox.bottom.as_numeric(), + ] + + ctm = tuple(float(x) for x in ctm) # type: ignore[assignment] + new_x = [ + ctm[0] * corners[i] + ctm[2] * corners[i + 1] + ctm[4] + for i in range(0, 8, 2) + ] + new_y = [ + ctm[1] * corners[i] + ctm[3] * corners[i + 1] + ctm[5] + for i in range(0, 8, 2) + ] + + lowerleft = (min(new_x), min(new_y)) + upperright = (max(new_x), max(new_y)) + + self.mediabox.lower_left = lowerleft + self.mediabox.upper_right = upperright + + def scale(self, sx: float, sy: float) -> None: + """ + Scale a page by the given factors by applying a transformation matrix + to its content and updating the page size. + + This updates the mediabox, the cropbox, and the contents + of the page. + + Args: + sx: The scaling factor on horizontal axis. + sy: The scaling factor on vertical axis. + """ + self.add_transformation((sx, 0, 0, sy, 0, 0)) + self.cropbox = self.cropbox.scale(sx, sy) + self.artbox = self.artbox.scale(sx, sy) + self.bleedbox = self.bleedbox.scale(sx, sy) + self.trimbox = self.trimbox.scale(sx, sy) + self.mediabox = self.mediabox.scale(sx, sy) + + if PG.ANNOTS in self: + annotations = self[PG.ANNOTS] + if isinstance(annotations, ArrayObject): + for annotation in annotations: + annotation_obj = annotation.get_object() + if ADA.Rect in annotation_obj: + rectangle = annotation_obj[ADA.Rect] + if isinstance(rectangle, ArrayObject): + rectangle[0] = FloatObject(float(rectangle[0]) * sx) + rectangle[1] = FloatObject(float(rectangle[1]) * sy) + rectangle[2] = FloatObject(float(rectangle[2]) * sx) + rectangle[3] = FloatObject(float(rectangle[3]) * sy) + + if PG.VP in self: + viewport = self[PG.VP] + if isinstance(viewport, ArrayObject): + bbox = viewport[0]["/BBox"] + else: + bbox = viewport["/BBox"] # type: ignore + scaled_bbox = RectangleObject( + ( + float(bbox[0]) * sx, + float(bbox[1]) * sy, + float(bbox[2]) * sx, + float(bbox[3]) * sy, + ) + ) + if isinstance(viewport, ArrayObject): + self[NameObject(PG.VP)][NumberObject(0)][ # type: ignore + NameObject("/BBox") + ] = scaled_bbox + else: + self[NameObject(PG.VP)][NameObject("/BBox")] = scaled_bbox # type: ignore + + def scale_by(self, factor: float) -> None: + """ + Scale a page by the given factor by applying a transformation matrix to + its content and updating the page size. + + Args: + factor: The scaling factor (for both X and Y axis). + """ + self.scale(factor, factor) + + def scale_to(self, width: float, height: float) -> None: + """ + Scale a page to the specified dimensions by applying a transformation + matrix to its content and updating the page size. + + Args: + width: The new width. + height: The new height. + """ + sx = width / float(self.mediabox.width) + sy = height / float(self.mediabox.height) + self.scale(sx, sy) + + def compress_content_streams(self, level: int = -1) -> None: + """ + Compress the size of this page by joining all content streams and + applying a FlateDecode filter. + + However, it is possible that this function will perform no action if + content stream compression becomes "automatic". + """ + content = self.get_contents() + if content is not None: + content_obj = content.flate_encode(level) + try: + content.indirect_reference.pdf._objects[ # type: ignore + content.indirect_reference.idnum - 1 # type: ignore + ] = content_obj + except AttributeError: + if self.indirect_reference is not None and hasattr( + self.indirect_reference.pdf, "_add_object" + ): + self.replace_contents(content_obj) + else: + raise ValueError("Page must be part of a PdfWriter") + + @property + def page_number(self) -> Optional[int]: + """ + Read-only property which returns the page number within the PDF file. + + Returns: + int : page number; None if the page is not attached to a PDF. + """ + if self.indirect_reference is None: + return None + else: + try: + lst = self.indirect_reference.pdf.pages + return lst.index(self) + except ValueError: + return None + + def _debug_for_extract(self) -> str: # pragma: no cover + out = "" + for ope, op in ContentStream( + self["/Contents"].get_object(), self.pdf, "bytes" + ).operations: + if op == b"TJ": + s = [x for x in ope[0] if isinstance(x, str)] + else: + s = [] + out += op.decode("utf-8") + " " + "".join(s) + ope.__repr__() + "\n" + out += "\n=============================\n" + try: + for fo in self[PG.RESOURCES]["/Font"]: # type:ignore + out += fo + "\n" + out += self[PG.RESOURCES]["/Font"][fo].__repr__() + "\n" # type:ignore + try: + enc_repr = self[PG.RESOURCES]["/Font"][fo][ # type:ignore + "/Encoding" + ].__repr__() + out += enc_repr + "\n" + except Exception: + pass + try: + out += ( + self[PG.RESOURCES]["/Font"][fo][ # type:ignore + "/ToUnicode" + ] + .get_data() + .decode() + + "\n" + ) + except Exception: + pass + + except KeyError: + out += "No Font\n" + return out + + def _extract_text( + self, + obj: Any, + pdf: Any, + orientations: Tuple[int, ...] = (0, 90, 180, 270), + space_width: float = 200.0, + content_key: Optional[str] = PG.CONTENTS, + visitor_operand_before: Optional[Callable[[Any, Any, Any, Any], None]] = None, + visitor_operand_after: Optional[Callable[[Any, Any, Any, Any], None]] = None, + visitor_text: Optional[Callable[[Any, Any, Any, Any, Any], None]] = None, + ) -> str: + """ + See extract_text for most arguments. + + Args: + content_key: indicate the default key where to extract data + None = the object; this allow to reuse the function on XObject + default = "/Content" + """ + text: str = "" + output: str = "" + rtl_dir: bool = False # right-to-left + cmaps: Dict[ + str, + Tuple[ + str, float, Union[str, Dict[int, str]], Dict[str, str], DictionaryObject + ], + ] = {} + try: + objr = obj + while NameObject(PG.RESOURCES) not in objr: + # /Resources can be inherited sometimes so we look to parents + objr = objr["/Parent"].get_object() + # if no parents we will have no /Resources will be available + # => an exception will be raised + resources_dict = cast(DictionaryObject, objr[PG.RESOURCES]) + except Exception: + # no resources means no text is possible (no font) we consider the + # file as not damaged, no need to check for TJ or Tj + return "" + if "/Font" in resources_dict: + for f in cast(DictionaryObject, resources_dict["/Font"]): + cmaps[f] = build_char_map(f, space_width, obj) + cmap: Tuple[ + Union[str, Dict[int, str]], Dict[str, str], str, Optional[DictionaryObject] + ] = ( + "charmap", + {}, + "NotInitialized", + None, + ) # (encoding,CMAP,font resource name,dictionary-object of font) + try: + content = ( + obj[content_key].get_object() if isinstance(content_key, str) else obj + ) + if not isinstance(content, ContentStream): + content = ContentStream(content, pdf, "bytes") + except KeyError: # it means no content can be extracted(certainly empty page) + return "" + # Note: we check all strings are TextStringObjects. ByteStringObjects + # are strings where the byte->string encoding was unknown, so adding + # them to the text here would be gibberish. + + cm_matrix: List[float] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + cm_stack = [] + tm_matrix: List[float] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + + # cm/tm_prev stores the last modified matrices can be an intermediate position + cm_prev: List[float] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + tm_prev: List[float] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + + # memo_cm/tm will be used to store the position at the beginning of building the text + memo_cm: List[float] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + memo_tm: List[float] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + char_scale = 1.0 + space_scale = 1.0 + _space_width: float = 500.0 # will be set correctly at first Tf + TL = 0.0 + font_size = 12.0 # init just in case of + + def current_spacewidth() -> float: + return _space_width / 1000.0 + + def process_operation(operator: bytes, operands: List[Any]) -> None: + nonlocal cm_matrix, cm_stack, tm_matrix, cm_prev, tm_prev, memo_cm, memo_tm + nonlocal char_scale, space_scale, _space_width, TL, font_size, cmap + nonlocal orientations, rtl_dir, visitor_text, output, text + global CUSTOM_RTL_MIN, CUSTOM_RTL_MAX, CUSTOM_RTL_SPECIAL_CHARS + + check_crlf_space: bool = False + # Table 5.4 page 405 + if operator == b"BT": + tm_matrix = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + output += text + if visitor_text is not None: + visitor_text(text, memo_cm, memo_tm, cmap[3], font_size) + text = "" + memo_cm = cm_matrix.copy() + memo_tm = tm_matrix.copy() + return None + elif operator == b"ET": + output += text + if visitor_text is not None: + visitor_text(text, memo_cm, memo_tm, cmap[3], font_size) + text = "" + memo_cm = cm_matrix.copy() + memo_tm = tm_matrix.copy() + # table 4.7 "Graphics state operators", page 219 + # cm_matrix calculation is a reserved for the moment + elif operator == b"q": + cm_stack.append( + ( + cm_matrix, + cmap, + font_size, + char_scale, + space_scale, + _space_width, + TL, + ) + ) + elif operator == b"Q": + try: + ( + cm_matrix, + cmap, + font_size, + char_scale, + space_scale, + _space_width, + TL, + ) = cm_stack.pop() + except Exception: + cm_matrix = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + elif operator == b"cm": + output += text + if visitor_text is not None: + visitor_text(text, memo_cm, memo_tm, cmap[3], font_size) + text = "" + cm_matrix = mult( + [ + float(operands[0]), + float(operands[1]), + float(operands[2]), + float(operands[3]), + float(operands[4]), + float(operands[5]), + ], + cm_matrix, + ) + memo_cm = cm_matrix.copy() + memo_tm = tm_matrix.copy() + # Table 5.2 page 398 + elif operator == b"Tz": + char_scale = float(operands[0]) / 100.0 + elif operator == b"Tw": + space_scale = 1.0 + float(operands[0]) + elif operator == b"TL": + TL = float(operands[0]) + elif operator == b"Tf": + if text != "": + output += text # .translate(cmap) + if visitor_text is not None: + visitor_text(text, memo_cm, memo_tm, cmap[3], font_size) + text = "" + memo_cm = cm_matrix.copy() + memo_tm = tm_matrix.copy() + try: + # charMapTuple: font_type, float(sp_width / 2), encoding, + # map_dict, font-dictionary + charMapTuple = cmaps[operands[0]] + _space_width = charMapTuple[1] + # current cmap: encoding, map_dict, font resource name + # (internal name, not the real font-name), + # font-dictionary. The font-dictionary describes the font. + cmap = ( + charMapTuple[2], + charMapTuple[3], + operands[0], + charMapTuple[4], + ) + except KeyError: # font not found + _space_width = unknown_char_map[1] + cmap = ( + unknown_char_map[2], + unknown_char_map[3], + f"???{operands[0]}", + None, + ) + try: + font_size = float(operands[1]) + except Exception: + pass # keep previous size + # Table 5.5 page 406 + elif operator == b"Td": + check_crlf_space = True + # A special case is a translating only tm: + # tm[0..5] = 1 0 0 1 e f, + # i.e. tm[4] += tx, tm[5] += ty. + tx = float(operands[0]) + ty = float(operands[1]) + tm_matrix[4] += tx * tm_matrix[0] + ty * tm_matrix[2] + tm_matrix[5] += tx * tm_matrix[1] + ty * tm_matrix[3] + elif operator == b"Tm": + check_crlf_space = True + tm_matrix = [ + float(operands[0]), + float(operands[1]), + float(operands[2]), + float(operands[3]), + float(operands[4]), + float(operands[5]), + ] + elif operator == b"T*": + check_crlf_space = True + tm_matrix[5] -= TL + + elif operator == b"Tj": + check_crlf_space = True + text, rtl_dir = handle_tj( + text, + operands, + cm_matrix, + tm_matrix, # text matrix + cmap, + orientations, + output, + font_size, + rtl_dir, + visitor_text, + ) + else: + return None + if check_crlf_space: + try: + text, output, cm_prev, tm_prev = crlf_space_check( + text, + (cm_prev, tm_prev), + (cm_matrix, tm_matrix), + (memo_cm, memo_tm), + cmap, + orientations, + output, + font_size, + visitor_text, + current_spacewidth(), + ) + if text == "": + memo_cm = cm_matrix.copy() + memo_tm = tm_matrix.copy() + except OrientationNotFoundError: + return None + + for operands, operator in content.operations: + if visitor_operand_before is not None: + visitor_operand_before(operator, operands, cm_matrix, tm_matrix) + # multiple operators are defined in here #### + if operator == b"'": + process_operation(b"T*", []) + process_operation(b"Tj", operands) + elif operator == b'"': + process_operation(b"Tw", [operands[0]]) + process_operation(b"Tc", [operands[1]]) + process_operation(b"T*", []) + process_operation(b"Tj", operands[2:]) + elif operator == b"TD": + process_operation(b"TL", [-operands[1]]) + process_operation(b"Td", operands) + elif operator == b"TJ": + for op in operands[0]: + if isinstance(op, (str, bytes)): + process_operation(b"Tj", [op]) + if isinstance(op, (int, float, NumberObject, FloatObject)) and ( + (abs(float(op)) >= _space_width) + and (len(text) > 0) + and (text[-1] != " ") + ): + process_operation(b"Tj", [" "]) + elif operator == b"Do": + output += text + if visitor_text is not None: + visitor_text(text, memo_cm, memo_tm, cmap[3], font_size) + try: + if output[-1] != "\n": + output += "\n" + if visitor_text is not None: + visitor_text( + "\n", + memo_cm, + memo_tm, + cmap[3], + font_size, + ) + except IndexError: + pass + try: + xobj = resources_dict["/XObject"] + if xobj[operands[0]]["/Subtype"] != "/Image": # type: ignore + text = self.extract_xform_text( + xobj[operands[0]], # type: ignore + orientations, + space_width, + visitor_operand_before, + visitor_operand_after, + visitor_text, + ) + output += text + if visitor_text is not None: + visitor_text( + text, + memo_cm, + memo_tm, + cmap[3], + font_size, + ) + except Exception: + logger_warning( + f" impossible to decode XFormObject {operands[0]}", + __name__, + ) + finally: + text = "" + memo_cm = cm_matrix.copy() + memo_tm = tm_matrix.copy() + + else: + process_operation(operator, operands) + if visitor_operand_after is not None: + visitor_operand_after(operator, operands, cm_matrix, tm_matrix) + output += text # just in case of + if text != "" and visitor_text is not None: + visitor_text(text, memo_cm, memo_tm, cmap[3], font_size) + return output + + def _layout_mode_fonts(self) -> Dict[str, _layout_mode.Font]: + """ + Get fonts formatted for "layout" mode text extraction. + + Returns: + Dict[str, Font]: dictionary of _layout_mode.Font instances keyed by font name + """ + # Font retrieval logic adapted from pypdf.PageObject._extract_text() + objr: Any = self + fonts: Dict[str, _layout_mode.Font] = {} + while objr is not None: + try: + resources_dict: Any = objr[PG.RESOURCES] + except KeyError: + resources_dict = {} + if "/Font" in resources_dict and self.pdf is not None: + for font_name in resources_dict["/Font"]: + *cmap, font_dict_obj = build_char_map(font_name, 200.0, self) + font_dict = { + k: v.get_object() + if isinstance(v, IndirectObject) + else [_v.get_object() for _v in v] + if isinstance(v, ArrayObject) + else v + for k, v in font_dict_obj.items() + } + # mypy really sucks at unpacking + fonts[font_name] = _layout_mode.Font(*cmap, font_dict) # type: ignore[call-arg,arg-type] + try: + objr = objr["/Parent"].get_object() + except KeyError: + objr = None + + return fonts + + def _layout_mode_text( + self, + space_vertically: bool = True, + scale_weight: float = 1.25, + strip_rotated: bool = True, + debug_path: Optional[Path] = None, + ) -> str: + """ + Get text preserving fidelity to source PDF text layout. + + Args: + space_vertically: include blank lines inferred from y distance + font + height. Defaults to True. + scale_weight: multiplier for string length when calculating weighted + average character width. Defaults to 1.25. + strip_rotated: Removes text that is rotated w.r.t. to the page from + layout mode output. Defaults to True. + debug_path (Path | None): if supplied, must target a directory. + creates the following files with debug information for layout mode + functions if supplied: + - fonts.json: output of self._layout_mode_fonts + - tjs.json: individual text render ops with corresponding transform matrices + - bts.json: text render ops left justified and grouped by BT/ET operators + - bt_groups.json: BT/ET operations grouped by rendered y-coord (aka lines) + Defaults to None. + + Returns: + str: multiline string containing page text in a fixed width format that + closely adheres to the rendered layout in the source pdf. + """ + fonts = self._layout_mode_fonts() + if debug_path: # pragma: no cover + import json + + debug_path.joinpath("fonts.json").write_text( + json.dumps( + fonts, indent=2, default=lambda x: getattr(x, "to_dict", str)(x) + ), + "utf-8", + ) + + ops = iter( + ContentStream(self["/Contents"].get_object(), self.pdf, "bytes").operations + ) + bt_groups = _layout_mode.text_show_operations( + ops, fonts, strip_rotated, debug_path + ) + + if not bt_groups: + return "" + + ty_groups = _layout_mode.y_coordinate_groups(bt_groups, debug_path) + + char_width = _layout_mode.fixed_char_width(bt_groups, scale_weight) + + return _layout_mode.fixed_width_page(ty_groups, char_width, space_vertically) + + def extract_text( + self, + *args: Any, + orientations: Union[int, Tuple[int, ...]] = (0, 90, 180, 270), + space_width: float = 200.0, + visitor_operand_before: Optional[Callable[[Any, Any, Any, Any], None]] = None, + visitor_operand_after: Optional[Callable[[Any, Any, Any, Any], None]] = None, + visitor_text: Optional[Callable[[Any, Any, Any, Any, Any], None]] = None, + extraction_mode: Literal["plain", "layout"] = "plain", + **kwargs: Any, + ) -> str: + """ + Locate all text drawing commands, in the order they are provided in the + content stream, and extract the text. + + This works well for some PDF files, but poorly for others, depending on + the generator used. This will be refined in the future. + + Do not rely on the order of text coming out of this function, as it + will change if this function is made more sophisticated. + + Arabic and Hebrew are extracted in the correct order. + If required a custom RTL range of characters can be defined; + see function set_custom_rtl. + + Additionally you can provide visitor methods to get informed on all + operations and all text objects. + For example in some PDF files this can be useful to parse tables. + + Args: + orientations: list of orientations extract_text will look for + default = (0, 90, 180, 270) + note: currently only 0 (up),90 (turned left), 180 (upside down), + 270 (turned right) + Silently ignored in "layout" mode. + space_width: force default space width + if not extracted from font (default: 200) + Silently ignored in "layout" mode. + visitor_operand_before: function to be called before processing an operation. + It has four arguments: operator, operand-arguments, + current transformation matrix and text matrix. + Ignored with a warning in "layout" mode. + visitor_operand_after: function to be called after processing an operation. + It has four arguments: operator, operand-arguments, + current transformation matrix and text matrix. + Ignored with a warning in "layout" mode. + visitor_text: function to be called when extracting some text at some position. + It has five arguments: text, current transformation matrix, + text matrix, font-dictionary and font-size. + The font-dictionary may be None in case of unknown fonts. + If not None it may e.g. contain key "/BaseFont" with value "/Arial,Bold". + Ignored with a warning in "layout" mode. + extraction_mode (Literal["plain", "layout"]): "plain" for legacy functionality, + "layout" for experimental layout mode functionality. + NOTE: orientations, space_width, and visitor_* parameters are NOT respected + in "layout" mode. + + kwargs: + layout_mode_space_vertically (bool): include blank lines inferred from + y distance + font height. Defaults to True. + layout_mode_scale_weight (float): multiplier for string length when calculating + weighted average character width. Defaults to 1.25. + layout_mode_strip_rotated (bool): layout mode does not support rotated text. + Set to False to include rotated text anyway. If rotated text is discovered, + layout will be degraded and a warning will result. Defaults to True. + layout_mode_debug_path (Path | None): if supplied, must target a directory. + creates the following files with debug information for layout mode + functions if supplied: + + - fonts.json: output of self._layout_mode_fonts + - tjs.json: individual text render ops with corresponding transform matrices + - bts.json: text render ops left justified and grouped by BT/ET operators + - bt_groups.json: BT/ET operations grouped by rendered y-coord (aka lines) + + Returns: + The extracted text + """ + if extraction_mode not in ["plain", "layout"]: + raise ValueError(f"Invalid text extraction mode '{extraction_mode}'") + if extraction_mode == "layout": + for visitor in ( + "visitor_operand_before", + "visitor_operand_after", + "visitor_text", + ): + if locals()[visitor]: + logger_warning( + f"Argument {visitor} is ignored in layout mode", + __name__, + ) + return self._layout_mode_text( + space_vertically=kwargs.get("layout_mode_space_vertically", True), + scale_weight=kwargs.get("layout_mode_scale_weight", 1.25), + strip_rotated=kwargs.get("layout_mode_strip_rotated", True), + debug_path=kwargs.get("layout_mode_debug_path", None), + ) + if len(args) >= 1: + if isinstance(args[0], str): + if len(args) >= 3: + if isinstance(args[2], (tuple, int)): + orientations = args[2] + else: + raise TypeError(f"Invalid positional parameter {args[2]}") + if len(args) >= 4: + if isinstance(args[3], (float, int)): + space_width = args[3] + else: + raise TypeError(f"Invalid positional parameter {args[3]}") + elif isinstance(args[0], (tuple, int)): + orientations = args[0] + if len(args) >= 2: + if isinstance(args[1], (float, int)): + space_width = args[1] + else: + raise TypeError(f"Invalid positional parameter {args[1]}") + else: + raise TypeError(f"Invalid positional parameter {args[0]}") + + if isinstance(orientations, int): + orientations = (orientations,) + + return self._extract_text( + self, + self.pdf, + orientations, + space_width, + PG.CONTENTS, + visitor_operand_before, + visitor_operand_after, + visitor_text, + ) + + def extract_xform_text( + self, + xform: EncodedStreamObject, + orientations: Tuple[int, ...] = (0, 90, 270, 360), + space_width: float = 200.0, + visitor_operand_before: Optional[Callable[[Any, Any, Any, Any], None]] = None, + visitor_operand_after: Optional[Callable[[Any, Any, Any, Any], None]] = None, + visitor_text: Optional[Callable[[Any, Any, Any, Any, Any], None]] = None, + ) -> str: + """ + Extract text from an XObject. + + Args: + xform: + orientations: + space_width: force default space width (if not extracted from font (default 200) + visitor_operand_before: + visitor_operand_after: + visitor_text: + + Returns: + The extracted text + """ + return self._extract_text( + xform, + self.pdf, + orientations, + space_width, + None, + visitor_operand_before, + visitor_operand_after, + visitor_text, + ) + + def _get_fonts(self) -> Tuple[Set[str], Set[str]]: + """ + Get the names of embedded fonts and unembedded fonts. + + Returns: + A tuple (Set of embedded fonts, set of unembedded fonts) + """ + obj = self.get_object() + assert isinstance(obj, DictionaryObject) + fonts: Set[str] = set() + embedded: Set[str] = set() + fonts, embedded = _get_fonts_walk(obj, fonts, embedded) + unembedded = fonts - embedded + return embedded, unembedded + + mediabox = _create_rectangle_accessor(PG.MEDIABOX, ()) + """A :class:`RectangleObject`, expressed in + default user space units, defining the boundaries of the physical medium on + which the page is intended to be displayed or printed.""" + + cropbox = _create_rectangle_accessor("/CropBox", (PG.MEDIABOX,)) + """ + A :class:`RectangleObject`, expressed in + default user space units, defining the visible region of default user + space. + + When the page is displayed or printed, its contents are to be clipped + (cropped) to this rectangle and then imposed on the output medium in some + implementation-defined manner. Default value: same as + :attr:`mediabox`. + """ + + bleedbox = _create_rectangle_accessor("/BleedBox", ("/CropBox", PG.MEDIABOX)) + """A :class:`RectangleObject`, expressed in + default user space units, defining the region to which the contents of the + page should be clipped when output in a production environment.""" + + trimbox = _create_rectangle_accessor("/TrimBox", ("/CropBox", PG.MEDIABOX)) + """A :class:`RectangleObject`, expressed in + default user space units, defining the intended dimensions of the finished + page after trimming.""" + + artbox = _create_rectangle_accessor("/ArtBox", ("/CropBox", PG.MEDIABOX)) + """A :class:`RectangleObject`, expressed in + default user space units, defining the extent of the page's meaningful + content as intended by the page's creator.""" + + @property + def annotations(self) -> Optional[ArrayObject]: + if "/Annots" not in self: + return None + else: + return cast(ArrayObject, self["/Annots"]) + + @annotations.setter + def annotations(self, value: Optional[ArrayObject]) -> None: + """ + Set the annotations array of the page. + + Typically you do not want to set this value, but append to it. + If you append to it, remember to add the object first to the writer + and only add the indirect object. + """ + if value is None: + del self[NameObject("/Annots")] + else: + self[NameObject("/Annots")] = value + + +class _VirtualList(Sequence[PageObject]): + def __init__( + self, + length_function: Callable[[], int], + get_function: Callable[[int], PageObject], + ) -> None: + self.length_function = length_function + self.get_function = get_function + self.current = -1 + + def __len__(self) -> int: + return self.length_function() + + @overload + def __getitem__(self, index: int) -> PageObject: + ... + + @overload + def __getitem__(self, index: slice) -> Sequence[PageObject]: + ... + + def __getitem__( + self, index: Union[int, slice] + ) -> Union[PageObject, Sequence[PageObject]]: + if isinstance(index, slice): + indices = range(*index.indices(len(self))) + cls = type(self) + return cls(indices.__len__, lambda idx: self[indices[idx]]) + if not isinstance(index, int): + raise TypeError("sequence indices must be integers") + len_self = len(self) + if index < 0: + # support negative indexes + index = len_self + index + if index < 0 or index >= len_self: + raise IndexError("sequence index out of range") + return self.get_function(index) + + def __delitem__(self, index: Union[int, slice]) -> None: + if isinstance(index, slice): + r = list(range(*index.indices(len(self)))) + # pages have to be deleted from last to first + r.sort() + r.reverse() + for p in r: + del self[p] # recursive call + return + if not isinstance(index, int): + raise TypeError("index must be integers") + len_self = len(self) + if index < 0: + # support negative indexes + index = len_self + index + if index < 0 or index >= len_self: + raise IndexError("index out of range") + ind = self[index].indirect_reference + assert ind is not None + parent: Optional[PdfObject] = cast(DictionaryObject, ind.get_object()).get( + "/Parent", None + ) + first = True + while parent is not None: + parent = cast(DictionaryObject, parent.get_object()) + try: + i = cast(ArrayObject, parent["/Kids"]).index(ind) + del cast(ArrayObject, parent["/Kids"])[i] + first = False + try: + assert ind is not None + del ind.pdf.flattened_pages[index] # case of page in a Reader + except Exception: # pragma: no cover + pass + if "/Count" in parent: + parent[NameObject("/Count")] = NumberObject( + cast(int, parent["/Count"]) - 1 + ) + if len(cast(ArrayObject, parent["/Kids"])) == 0: + # No more objects in this part of this sub tree + ind = parent.indirect_reference + parent = parent.get("/Parent", None) + except ValueError: # from index + if first: + raise PdfReadError(f"Page not found in page tree: {ind}") + break + + def __iter__(self) -> Iterator[PageObject]: + for i in range(len(self)): + yield self[i] + + def __str__(self) -> str: + p = [f"PageObject({i})" for i in range(self.length_function())] + return f"[{', '.join(p)}]" + + +def _get_fonts_walk( + obj: DictionaryObject, + fnt: Set[str], + emb: Set[str], +) -> Tuple[Set[str], Set[str]]: + """ + Get the set of all fonts and all embedded fonts. + + Args: + obj: Page resources dictionary + fnt: font + emb: embedded fonts + + Returns: + A tuple (fnt, emb) + + If there is a key called 'BaseFont', that is a font that is used in the document. + If there is a key called 'FontName' and another key in the same dictionary object + that is called 'FontFilex' (where x is null, 2, or 3), then that fontname is + embedded. + + We create and add to two sets, fnt = fonts used and emb = fonts embedded. + """ + fontkeys = ("/FontFile", "/FontFile2", "/FontFile3") + + def process_font(f: DictionaryObject) -> None: + nonlocal fnt, emb + f = cast(DictionaryObject, f.get_object()) # to be sure + if "/BaseFont" in f: + fnt.add(cast(str, f["/BaseFont"])) + + if ( + ("/CharProcs" in f) + or ( + "/FontDescriptor" in f + and any( + x in cast(DictionaryObject, f["/FontDescriptor"]) for x in fontkeys + ) + ) + or ( + "/DescendantFonts" in f + and "/FontDescriptor" + in cast( + DictionaryObject, + cast(ArrayObject, f["/DescendantFonts"])[0].get_object(), + ) + and any( + x + in cast( + DictionaryObject, + cast( + DictionaryObject, + cast(ArrayObject, f["/DescendantFonts"])[0].get_object(), + )["/FontDescriptor"], + ) + for x in fontkeys + ) + ) + ): + # the list comprehension ensures there is FontFile + try: + emb.add(cast(str, f["/BaseFont"])) + except KeyError: + emb.add("(" + cast(str, f["/Subtype"]) + ")") + + if "/DR" in obj and "/Font" in cast(DictionaryObject, obj["/DR"]): + for f in cast(DictionaryObject, cast(DictionaryObject, obj["/DR"])["/Font"]): + process_font(f) + if "/Resources" in obj: + if "/Font" in cast(DictionaryObject, obj["/Resources"]): + for f in cast( + DictionaryObject, cast(DictionaryObject, obj["/Resources"])["/Font"] + ).values(): + process_font(f) + if "/XObject" in cast(DictionaryObject, obj["/Resources"]): + for x in cast( + DictionaryObject, cast(DictionaryObject, obj["/Resources"])["/XObject"] + ).values(): + _get_fonts_walk(cast(DictionaryObject, x.get_object()), fnt, emb) + if "/Annots" in obj: + for a in cast(ArrayObject, obj["/Annots"]): + _get_fonts_walk(cast(DictionaryObject, a.get_object()), fnt, emb) + if "/AP" in obj: + if ( + cast(DictionaryObject, cast(DictionaryObject, obj["/AP"])["/N"]).get( + "/Type" + ) + == "/XObject" + ): + _get_fonts_walk( + cast(DictionaryObject, cast(DictionaryObject, obj["/AP"])["/N"]), + fnt, + emb, + ) + else: + for a in cast(DictionaryObject, cast(DictionaryObject, obj["/AP"])["/N"]): + _get_fonts_walk(cast(DictionaryObject, a), fnt, emb) + return fnt, emb # return the sets for each page diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_page_labels.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_page_labels.py new file mode 100644 index 00000000..1bedc003 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_page_labels.py @@ -0,0 +1,285 @@ +""" +Page labels are shown by PDF viewers as "the page number". + +A page has a numeric index, starting at 0. Additionally, the page +has a label. In the most simple case: + + label = index + 1 + +However, the title page and the table of contents might have Roman numerals as +page labels. This makes things more complicated. + +Example 1 +--------- + +>>> reader.root_object["/PageLabels"]["/Nums"] +[0, IndirectObject(18, 0, 139929798197504), + 8, IndirectObject(19, 0, 139929798197504)] +>>> reader.get_object(reader.root_object["/PageLabels"]["/Nums"][1]) +{'/S': '/r'} +>>> reader.get_object(reader.root_object["/PageLabels"]["/Nums"][3]) +{'/S': '/D'} + +Example 2 +--------- +The following is a document with pages labeled +i, ii, iii, iv, 1, 2, 3, A-8, A-9, ... + +1 0 obj + << /Type /Catalog + /PageLabels << /Nums [ + 0 << /S /r >> + 4 << /S /D >> + 7 << /S /D + /P ( A- ) + /St 8 + >> + % A number tree containing + % three page label dictionaries + ] + >> + ... + >> +endobj + + +§12.4.2 PDF Specification 1.7 and 2.0 +===================================== + +Entries in a page label dictionary +---------------------------------- +The /S key: +D Decimal Arabic numerals +R Uppercase Roman numerals +r Lowercase Roman numerals +A Uppercase letters (A to Z for the first 26 pages, + AA to ZZ for the next 26, and so on) +a Lowercase letters (a to z for the first 26 pages, + aa to zz for the next 26, and so on) +""" + +from typing import Iterator, List, Optional, Tuple, cast + +from ._protocols import PdfCommonDocProtocol +from ._utils import logger_warning +from .generic import ( + ArrayObject, + DictionaryObject, + NullObject, + NumberObject, + is_null_or_none, +) + + +def number2uppercase_roman_numeral(num: int) -> str: + roman = [ + (1000, "M"), + (900, "CM"), + (500, "D"), + (400, "CD"), + (100, "C"), + (90, "XC"), + (50, "L"), + (40, "XL"), + (10, "X"), + (9, "IX"), + (5, "V"), + (4, "IV"), + (1, "I"), + ] + + def roman_num(num: int) -> Iterator[str]: + for decimal, roman_repr in roman: + x, _ = divmod(num, decimal) + yield roman_repr * x + num -= decimal * x + if num <= 0: + break + + return "".join(list(roman_num(num))) + + +def number2lowercase_roman_numeral(number: int) -> str: + return number2uppercase_roman_numeral(number).lower() + + +def number2uppercase_letter(number: int) -> str: + if number <= 0: + raise ValueError("Expecting a positive number") + alphabet = [chr(i) for i in range(ord("A"), ord("Z") + 1)] + rep = "" + while number > 0: + remainder = number % 26 + if remainder == 0: + remainder = 26 + rep = alphabet[remainder - 1] + rep + # update + number -= remainder + number = number // 26 + return rep + + +def number2lowercase_letter(number: int) -> str: + return number2uppercase_letter(number).lower() + + +def get_label_from_nums(dictionary_object: DictionaryObject, index: int) -> str: + # [Nums] shall be an array of the form + # [ key 1 value 1 key 2 value 2 ... key n value n ] + # where each key_i is an integer and the corresponding + # value_i shall be the object associated with that key. + # The keys shall be sorted in numerical order, + # analogously to the arrangement of keys in a name tree + # as described in 7.9.6, "Name Trees." + nums = cast(ArrayObject, dictionary_object["/Nums"]) + i = 0 + value = None + start_index = 0 + while i < len(nums): + start_index = nums[i] + value = nums[i + 1].get_object() + if i + 2 == len(nums): + break + if nums[i + 2] > index: + break + i += 2 + m = { + None: lambda n: "", + "/D": lambda n: str(n), + "/R": number2uppercase_roman_numeral, + "/r": number2lowercase_roman_numeral, + "/A": number2uppercase_letter, + "/a": number2lowercase_letter, + } + # if /Nums array is not following the specification or if /Nums is empty + if not isinstance(value, dict): + return str(index + 1) # Fallback + start = value.get("/St", 1) + prefix = value.get("/P", "") + return prefix + m[value.get("/S")](index - start_index + start) + + +def index2label(reader: PdfCommonDocProtocol, index: int) -> str: + """ + See 7.9.7 "Number Trees". + + Args: + reader: The PdfReader + index: The index of the page + + Returns: + The label of the page, e.g. "iv" or "4". + """ + root = cast(DictionaryObject, reader.root_object) + if "/PageLabels" not in root: + return str(index + 1) # Fallback + number_tree = cast(DictionaryObject, root["/PageLabels"].get_object()) + if "/Nums" in number_tree: + return get_label_from_nums(number_tree, index) + if "/Kids" in number_tree and not isinstance(number_tree["/Kids"], NullObject): + # number_tree = {'/Kids': [IndirectObject(7333, 0, 140132998195856), ...]} + # Limit maximum depth. + level = 0 + while level < 100: + kids = cast(List[DictionaryObject], number_tree["/Kids"]) + for kid in kids: + # kid = {'/Limits': [0, 63], '/Nums': [0, {'/P': 'C1'}, ...]} + limits = cast(List[int], kid["/Limits"]) + if limits[0] <= index <= limits[1]: + if not is_null_or_none(kid.get("/Kids", None)): + # Recursive definition. + level += 1 + if level == 100: # pragma: no cover + raise NotImplementedError( + "Too deep nesting is not supported." + ) + number_tree = kid + # Exit the inner `for` loop and continue at the next level with the + # next iteration of the `while` loop. + break + return get_label_from_nums(kid, index) + else: + # When there are no kids, make sure to exit the `while` loop directly + # and continue with the fallback. + break + + logger_warning(f"Could not reliably determine page label for {index}.", __name__) + return str(index + 1) # Fallback if neither /Nums nor /Kids is in the number_tree + + +def nums_insert( + key: NumberObject, + value: DictionaryObject, + nums: ArrayObject, +) -> None: + """ + Insert a key, value pair in a Nums array. + + See 7.9.7 "Number Trees". + + Args: + key: number key of the entry + value: value of the entry + nums: Nums array to modify + """ + if len(nums) % 2 != 0: + raise ValueError("a nums like array must have an even number of elements") + + i = len(nums) + while i != 0 and key <= nums[i - 2]: + i = i - 2 + + if i < len(nums) and key == nums[i]: + nums[i + 1] = value + else: + nums.insert(i, key) + nums.insert(i + 1, value) + + +def nums_clear_range( + key: NumberObject, + page_index_to: int, + nums: ArrayObject, +) -> None: + """ + Remove all entries in a number tree in a range after an entry. + + See 7.9.7 "Number Trees". + + Args: + key: number key of the entry before the range + page_index_to: The page index of the upper limit of the range + nums: Nums array to modify + """ + if len(nums) % 2 != 0: + raise ValueError("a nums like array must have an even number of elements") + if page_index_to < key: + raise ValueError("page_index_to must be greater or equal than key") + + i = nums.index(key) + 2 + while i < len(nums) and nums[i] <= page_index_to: + nums.pop(i) + nums.pop(i) + + +def nums_next( + key: NumberObject, + nums: ArrayObject, +) -> Tuple[Optional[NumberObject], Optional[DictionaryObject]]: + """ + Return the (key, value) pair of the entry after the given one. + + See 7.9.7 "Number Trees". + + Args: + key: number key of the entry + nums: Nums array + """ + if len(nums) % 2 != 0: + raise ValueError("a nums like array must have an even number of elements") + + i = nums.index(key) + 2 + if i < len(nums): + return (nums[i], nums[i + 1]) + else: + return (None, None) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_protocols.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_protocols.py new file mode 100644 index 00000000..431db1a1 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_protocols.py @@ -0,0 +1,86 @@ +"""Helpers for working with PDF types.""" + +from abc import abstractmethod +from pathlib import Path +from typing import IO, Any, Dict, List, Optional, Protocol, Tuple, Union + +from ._utils import StrByteType, StreamType + + +class PdfObjectProtocol(Protocol): + indirect_reference: Any + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Union[Tuple[str, ...], List[str], None] = (), + ) -> Any: + ... # pragma: no cover + + def _reference_clone(self, clone: Any, pdf_dest: Any) -> Any: + ... # pragma: no cover + + def get_object(self) -> Optional["PdfObjectProtocol"]: + ... # pragma: no cover + + def hash_value(self) -> bytes: + ... # pragma: no cover + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + ... # pragma: no cover + + +class XmpInformationProtocol(PdfObjectProtocol): + pass + + +class PdfCommonDocProtocol(Protocol): + @property + def pdf_header(self) -> str: + ... # pragma: no cover + + @property + def pages(self) -> List[Any]: + ... # pragma: no cover + + @property + def root_object(self) -> PdfObjectProtocol: + ... # pragma: no cover + + def get_object(self, indirect_reference: Any) -> Optional[PdfObjectProtocol]: + ... # pragma: no cover + + @property + def strict(self) -> bool: + ... # pragma: no cover + + +class PdfReaderProtocol(PdfCommonDocProtocol, Protocol): + @property + @abstractmethod + def xref(self) -> Dict[int, Dict[int, Any]]: + ... # pragma: no cover + + @property + @abstractmethod + def trailer(self) -> Dict[str, Any]: + ... # pragma: no cover + + +class PdfWriterProtocol(PdfCommonDocProtocol, Protocol): + _objects: List[Any] + _id_translated: Dict[int, Dict[int, int]] + + incremental: bool + _reader: Any # PdfReader + + @abstractmethod + def write(self, stream: Union[Path, StrByteType]) -> Tuple[bool, IO[Any]]: + ... # pragma: no cover + + @abstractmethod + def _add_object(self, obj: Any) -> Any: + ... # pragma: no cover diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_reader.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_reader.py new file mode 100644 index 00000000..9948cbea --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_reader.py @@ -0,0 +1,1189 @@ +# Copyright (c) 2006, Mathieu Fenniak +# Copyright (c) 2007, Ashish Kulkarni +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import os +import re +from io import BytesIO, UnsupportedOperation +from pathlib import Path +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Tuple, + Type, + Union, + cast, +) + +from ._doc_common import PdfDocCommon, convert_to_int +from ._encryption import Encryption, PasswordType +from ._utils import ( + StrByteType, + StreamType, + logger_warning, + read_non_whitespace, + read_previous_line, + read_until_whitespace, + skip_over_comment, + skip_over_whitespace, +) +from .constants import TrailerKeys as TK +from .errors import ( + EmptyFileError, + FileNotDecryptedError, + PdfReadError, + PdfStreamError, + WrongPasswordError, +) +from .generic import ( + ArrayObject, + ContentStream, + DecodedStreamObject, + DictionaryObject, + EncodedStreamObject, + IndirectObject, + NameObject, + NullObject, + NumberObject, + PdfObject, + StreamObject, + TextStringObject, + is_null_or_none, + read_object, +) +from .xmp import XmpInformation + +if TYPE_CHECKING: + from ._page import PageObject + + +class PdfReader(PdfDocCommon): + """ + Initialize a PdfReader object. + + This operation can take some time, as the PDF stream's cross-reference + tables are read into memory. + + Args: + stream: A File object or an object that supports the standard read + and seek methods similar to a File object. Could also be a + string representing a path to a PDF file. + strict: Determines whether user should be warned of all + problems and also causes some correctable problems to be fatal. + Defaults to ``False``. + password: Decrypt PDF file at initialization. If the + password is None, the file will not be decrypted. + Defaults to ``None``. + """ + + def __init__( + self, + stream: Union[StrByteType, Path], + strict: bool = False, + password: Union[None, str, bytes] = None, + ) -> None: + self.strict = strict + self.flattened_pages: Optional[List[PageObject]] = None + #: Storage of parsed PDF objects. + self.resolved_objects: Dict[Tuple[Any, Any], Optional[PdfObject]] = {} + + self.xref_index = 0 + self.xref: Dict[int, Dict[Any, Any]] = {} + self.xref_free_entry: Dict[int, Dict[Any, Any]] = {} + self.xref_objStm: Dict[int, Tuple[Any, Any]] = {} + self.trailer = DictionaryObject() + + self._page_id2num: Optional[ + Dict[Any, Any] + ] = None # map page indirect_reference number to Page Number + if hasattr(stream, "mode") and "b" not in stream.mode: + logger_warning( + "PdfReader stream/file object is not in binary mode. " + "It may not be read correctly.", + __name__, + ) + self._stream_opened = False + if isinstance(stream, (str, Path)): + with open(stream, "rb") as fh: + stream = BytesIO(fh.read()) + self._stream_opened = True + self._startxref: int = 0 + self.read(stream) + self.stream = stream + + self._override_encryption = False + self._encryption: Optional[Encryption] = None + if self.is_encrypted: + self._override_encryption = True + # Some documents may not have a /ID, use two empty + # byte strings instead. Solves + # https://github.com/py-pdf/pypdf/issues/608 + id_entry = self.trailer.get(TK.ID) + id1_entry = id_entry[0].get_object().original_bytes if id_entry else b"" + encrypt_entry = cast( + DictionaryObject, self.trailer[TK.ENCRYPT].get_object() + ) + self._encryption = Encryption.read(encrypt_entry, id1_entry) + + # try empty password if no password provided + pwd = password if password is not None else b"" + if ( + self._encryption.verify(pwd) == PasswordType.NOT_DECRYPTED + and password is not None + ): + # raise if password provided + raise WrongPasswordError("Wrong password") + self._override_encryption = False + elif password is not None: + raise PdfReadError("Not encrypted file") + + def __enter__(self) -> "PdfReader": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self.close() + + def close(self) -> None: + """Close the stream if opened in __init__ and clear memory.""" + if self._stream_opened: + self.stream.close() + self.flattened_pages = [] + self.resolved_objects = {} + self.trailer = DictionaryObject() + self.xref = {} + self.xref_free_entry = {} + self.xref_objStm = {} + + @property + def root_object(self) -> DictionaryObject: + """Provide access to "/Root". Standardized with PdfWriter.""" + root = self.trailer[TK.ROOT] + if root is None: + raise PdfReadError('Cannot find "/Root" key in trailer') + return cast(DictionaryObject, root.get_object()) + + @property + def _info(self) -> Optional[DictionaryObject]: + """ + Provide access to "/Info". Standardized with PdfWriter. + + Returns: + /Info Dictionary; None if the entry does not exist + """ + info = self.trailer.get(TK.INFO, None) + if is_null_or_none(info): + return None + else: + info = info.get_object() + if info == None: # noqa: E711 + raise PdfReadError( + "Trailer not found or does not point to document information directory" + ) + return cast(DictionaryObject, info) + + @property + def _ID(self) -> Optional[ArrayObject]: + """ + Provide access to "/ID". Standardized with PdfWriter. + + Returns: + /ID array; None if the entry does not exist + """ + id = self.trailer.get(TK.ID, None) + return None if is_null_or_none(id) else cast(ArrayObject, id.get_object()) + + def _repr_mimebundle_( + self, + include: Union[None, Iterable[str]] = None, + exclude: Union[None, Iterable[str]] = None, + ) -> Dict[str, Any]: + """ + Integration into Jupyter Notebooks. + + This method returns a dictionary that maps a mime-type to its + representation. + + See https://ipython.readthedocs.io/en/stable/config/integrating.html + """ + self.stream.seek(0) + pdf_data = self.stream.read() + data = { + "application/pdf": pdf_data, + } + + if include is not None: + # Filter representations based on include list + data = {k: v for k, v in data.items() if k in include} + + if exclude is not None: + # Remove representations based on exclude list + data = {k: v for k, v in data.items() if k not in exclude} + + return data + + @property + def pdf_header(self) -> str: + """ + The first 8 bytes of the file. + + This is typically something like ``'%PDF-1.6'`` and can be used to + detect if the file is actually a PDF file and which version it is. + """ + # TODO: Make this return a bytes object for consistency + # but that needs a deprecation + loc = self.stream.tell() + self.stream.seek(0, 0) + pdf_file_version = self.stream.read(8).decode("utf-8", "backslashreplace") + self.stream.seek(loc, 0) # return to where it was + return pdf_file_version + + @property + def xmp_metadata(self) -> Optional[XmpInformation]: + """XMP (Extensible Metadata Platform) data.""" + try: + self._override_encryption = True + return cast(XmpInformation, self.root_object.xmp_metadata) + finally: + self._override_encryption = False + + def _get_page_number_by_indirect( + self, indirect_reference: Union[None, int, NullObject, IndirectObject] + ) -> Optional[int]: + """ + Generate _page_id2num. + + Args: + indirect_reference: + + Returns: + The page number or None + """ + if self._page_id2num is None: + self._page_id2num = { + x.indirect_reference.idnum: i for i, x in enumerate(self.pages) # type: ignore + } + + if is_null_or_none(indirect_reference): + return None + assert isinstance(indirect_reference, (int, IndirectObject)), "mypy" + if isinstance(indirect_reference, int): + idnum = indirect_reference + else: + idnum = indirect_reference.idnum + assert self._page_id2num is not None, "hint for mypy" + ret = self._page_id2num.get(idnum, None) + return ret + + def _get_object_from_stream( + self, indirect_reference: IndirectObject + ) -> Union[int, PdfObject, str]: + # indirect reference to object in object stream + # read the entire object stream into memory + stmnum, idx = self.xref_objStm[indirect_reference.idnum] + obj_stm: EncodedStreamObject = IndirectObject(stmnum, 0, self).get_object() # type: ignore + # This is an xref to a stream, so its type better be a stream + assert cast(str, obj_stm["/Type"]) == "/ObjStm" + stream_data = BytesIO(obj_stm.get_data()) + for i in range(obj_stm["/N"]): # type: ignore + read_non_whitespace(stream_data) + stream_data.seek(-1, 1) + objnum = NumberObject.read_from_stream(stream_data) + read_non_whitespace(stream_data) + stream_data.seek(-1, 1) + offset = NumberObject.read_from_stream(stream_data) + read_non_whitespace(stream_data) + stream_data.seek(-1, 1) + if objnum != indirect_reference.idnum: + # We're only interested in one object + continue + if self.strict and idx != i: + raise PdfReadError("Object is in wrong index.") + stream_data.seek(int(obj_stm["/First"] + offset), 0) # type: ignore + + # to cope with some case where the 'pointer' is on a white space + read_non_whitespace(stream_data) + stream_data.seek(-1, 1) + + try: + obj = read_object(stream_data, self) + except PdfStreamError as exc: + # Stream object cannot be read. Normally, a critical error, but + # Adobe Reader doesn't complain, so continue (in strict mode?) + logger_warning( + f"Invalid stream (index {i}) within object " + f"{indirect_reference.idnum} {indirect_reference.generation}: " + f"{exc}", + __name__, + ) + + if self.strict: # pragma: no cover + raise PdfReadError( + f"Cannot read object stream: {exc}" + ) # pragma: no cover + # Replace with null. Hopefully it's nothing important. + obj = NullObject() # pragma: no cover + return obj + + if self.strict: # pragma: no cover + raise PdfReadError( + "This is a fatal error in strict mode." + ) # pragma: no cover + return NullObject() # pragma: no cover + + def get_object( + self, indirect_reference: Union[int, IndirectObject] + ) -> Optional[PdfObject]: + if isinstance(indirect_reference, int): + indirect_reference = IndirectObject(indirect_reference, 0, self) + retval = self.cache_get_indirect_object( + indirect_reference.generation, indirect_reference.idnum + ) + if retval is not None: + return retval + if ( + indirect_reference.generation == 0 + and indirect_reference.idnum in self.xref_objStm + ): + retval = self._get_object_from_stream(indirect_reference) # type: ignore + elif ( + indirect_reference.generation in self.xref + and indirect_reference.idnum in self.xref[indirect_reference.generation] + ): + if self.xref_free_entry.get(indirect_reference.generation, {}).get( + indirect_reference.idnum, False + ): + return NullObject() + start = self.xref[indirect_reference.generation][indirect_reference.idnum] + self.stream.seek(start, 0) + try: + idnum, generation = self.read_object_header(self.stream) + if ( + idnum != indirect_reference.idnum + or generation != indirect_reference.generation + ): + raise PdfReadError("not matching, we parse the file for it") + except Exception: + if hasattr(self.stream, "getbuffer"): + buf = bytes(self.stream.getbuffer()) + else: + p = self.stream.tell() + self.stream.seek(0, 0) + buf = self.stream.read(-1) + self.stream.seek(p, 0) + m = re.search( + rf"\s{indirect_reference.idnum}\s+{indirect_reference.generation}\s+obj".encode(), + buf, + ) + if m is not None: + logger_warning( + f"Object ID {indirect_reference.idnum},{indirect_reference.generation} ref repaired", + __name__, + ) + self.xref[indirect_reference.generation][ + indirect_reference.idnum + ] = (m.start(0) + 1) + self.stream.seek(m.start(0) + 1) + idnum, generation = self.read_object_header(self.stream) + else: + idnum = -1 + generation = -1 # exception will be raised below + if idnum != indirect_reference.idnum and self.xref_index: + # Xref table probably had bad indexes due to not being zero-indexed + if self.strict: + raise PdfReadError( + f"Expected object ID ({indirect_reference.idnum} {indirect_reference.generation}) " + f"does not match actual ({idnum} {generation}); " + "xref table not zero-indexed." + ) + # xref table is corrected in non-strict mode + elif idnum != indirect_reference.idnum and self.strict: + # some other problem + raise PdfReadError( + f"Expected object ID ({indirect_reference.idnum} " + f"{indirect_reference.generation}) does not match actual " + f"({idnum} {generation})." + ) + if self.strict: + assert generation == indirect_reference.generation + retval = read_object(self.stream, self) # type: ignore + + # override encryption is used for the /Encrypt dictionary + if not self._override_encryption and self._encryption is not None: + # if we don't have the encryption key: + if not self._encryption.is_decrypted(): + raise FileNotDecryptedError("File has not been decrypted") + # otherwise, decrypt here... + retval = cast(PdfObject, retval) + retval = self._encryption.decrypt_object( + retval, indirect_reference.idnum, indirect_reference.generation + ) + else: + if hasattr(self.stream, "getbuffer"): + buf = bytes(self.stream.getbuffer()) + else: + p = self.stream.tell() + self.stream.seek(0, 0) + buf = self.stream.read(-1) + self.stream.seek(p, 0) + m = re.search( + rf"\s{indirect_reference.idnum}\s+{indirect_reference.generation}\s+obj".encode(), + buf, + ) + if m is not None: + logger_warning( + f"Object {indirect_reference.idnum} {indirect_reference.generation} found", + __name__, + ) + if indirect_reference.generation not in self.xref: + self.xref[indirect_reference.generation] = {} + self.xref[indirect_reference.generation][indirect_reference.idnum] = ( + m.start(0) + 1 + ) + self.stream.seek(m.end(0) + 1) + skip_over_whitespace(self.stream) + self.stream.seek(-1, 1) + retval = read_object(self.stream, self) # type: ignore + + # override encryption is used for the /Encrypt dictionary + if not self._override_encryption and self._encryption is not None: + # if we don't have the encryption key: + if not self._encryption.is_decrypted(): + raise FileNotDecryptedError("File has not been decrypted") + # otherwise, decrypt here... + retval = cast(PdfObject, retval) + retval = self._encryption.decrypt_object( + retval, indirect_reference.idnum, indirect_reference.generation + ) + else: + logger_warning( + f"Object {indirect_reference.idnum} {indirect_reference.generation} not defined.", + __name__, + ) + if self.strict: + raise PdfReadError("Could not find object.") + self.cache_indirect_object( + indirect_reference.generation, indirect_reference.idnum, retval + ) + return retval + + def read_object_header(self, stream: StreamType) -> Tuple[int, int]: + # Should never be necessary to read out whitespace, since the + # cross-reference table should put us in the right spot to read the + # object header. In reality some files have stupid cross reference + # tables that are off by whitespace bytes. + extra = False + skip_over_comment(stream) + extra |= skip_over_whitespace(stream) + stream.seek(-1, 1) + idnum = read_until_whitespace(stream) + extra |= skip_over_whitespace(stream) + stream.seek(-1, 1) + generation = read_until_whitespace(stream) + extra |= skip_over_whitespace(stream) + stream.seek(-1, 1) + + # although it's not used, it might still be necessary to read + _obj = stream.read(3) + + read_non_whitespace(stream) + stream.seek(-1, 1) + if extra and self.strict: + logger_warning( + f"Superfluous whitespace found in object header {idnum} {generation}", # type: ignore + __name__, + ) + return int(idnum), int(generation) + + def cache_get_indirect_object( + self, generation: int, idnum: int + ) -> Optional[PdfObject]: + try: + return self.resolved_objects.get((generation, idnum)) + except RecursionError: + raise PdfReadError("Maximum recursion depth reached.") + + def cache_indirect_object( + self, generation: int, idnum: int, obj: Optional[PdfObject] + ) -> Optional[PdfObject]: + if (generation, idnum) in self.resolved_objects: + msg = f"Overwriting cache for {generation} {idnum}" + if self.strict: + raise PdfReadError(msg) + logger_warning(msg, __name__) + self.resolved_objects[(generation, idnum)] = obj + if obj is not None: + obj.indirect_reference = IndirectObject(idnum, generation, self) + return obj + + def _replace_object(self, indirect: IndirectObject, obj: PdfObject) -> PdfObject: + # function reserved for future dev + if indirect.pdf != self: + raise ValueError("Cannot update PdfReader with external object") + if (indirect.generation, indirect.idnum) not in self.resolved_objects: + raise ValueError("Cannot find referenced object") + self.resolved_objects[(indirect.generation, indirect.idnum)] = obj + obj.indirect_reference = indirect + return obj + + def read(self, stream: StreamType) -> None: + self._basic_validation(stream) + self._find_eof_marker(stream) + startxref = self._find_startxref_pos(stream) + self._startxref = startxref + + # check and eventually correct the startxref only in not strict + xref_issue_nr = self._get_xref_issues(stream, startxref) + if xref_issue_nr != 0: + if self.strict and xref_issue_nr: + raise PdfReadError("Broken xref table") + logger_warning(f"incorrect startxref pointer({xref_issue_nr})", __name__) + + # read all cross reference tables and their trailers + self._read_xref_tables_and_trailers(stream, startxref, xref_issue_nr) + + # if not zero-indexed, verify that the table is correct; change it if necessary + if self.xref_index and not self.strict: + loc = stream.tell() + for gen, xref_entry in self.xref.items(): + if gen == 65535: + continue + xref_k = sorted( + xref_entry.keys() + ) # must ensure ascendant to prevent damage + for id in xref_k: + stream.seek(xref_entry[id], 0) + try: + pid, _pgen = self.read_object_header(stream) + except ValueError: + self._rebuild_xref_table(stream) + break + if pid == id - self.xref_index: + # fixing index item per item is required for revised PDF. + self.xref[gen][pid] = self.xref[gen][id] + del self.xref[gen][id] + # if not, then either it's just plain wrong, or the + # non-zero-index is actually correct + stream.seek(loc, 0) # return to where it was + + # remove wrong objects (not pointing to correct structures) - cf #2326 + if not self.strict: + loc = stream.tell() + for gen, xref_entry in self.xref.items(): + if gen == 65535: + continue + ids = list(xref_entry.keys()) + for id in ids: + stream.seek(xref_entry[id], 0) + try: + self.read_object_header(stream) + except ValueError: + logger_warning( + f"Ignoring wrong pointing object {id} {gen} (offset {xref_entry[id]})", + __name__, + ) + del xref_entry[id] # we can delete the id, we are parsing ids + stream.seek(loc, 0) # return to where it was + + def _basic_validation(self, stream: StreamType) -> None: + """Ensure file is not empty. Read at most 5 bytes.""" + stream.seek(0, os.SEEK_SET) + try: + header_byte = stream.read(5) + except UnicodeDecodeError: + raise UnsupportedOperation("cannot read header") + if header_byte == b"": + raise EmptyFileError("Cannot read an empty file") + elif header_byte != b"%PDF-": + if self.strict: + raise PdfReadError( + f"PDF starts with '{header_byte.decode('utf8')}', " + "but '%PDF-' expected" + ) + else: + logger_warning(f"invalid pdf header: {header_byte}", __name__) + stream.seek(0, os.SEEK_END) + + def _find_eof_marker(self, stream: StreamType) -> None: + """ + Jump to the %%EOF marker. + + According to the specs, the %%EOF marker should be at the very end of + the file. Hence for standard-compliant PDF documents this function will + read only the last part (DEFAULT_BUFFER_SIZE). + """ + HEADER_SIZE = 8 # to parse whole file, Header is e.g. '%PDF-1.6' + line = b"" + while line[:5] != b"%%EOF": + if stream.tell() < HEADER_SIZE: + if self.strict: + raise PdfReadError("EOF marker not found") + else: + logger_warning("EOF marker not found", __name__) + line = read_previous_line(stream) + + def _find_startxref_pos(self, stream: StreamType) -> int: + """ + Find startxref entry - the location of the xref table. + + Args: + stream: + + Returns: + The bytes offset + """ + line = read_previous_line(stream) + try: + startxref = int(line) + except ValueError: + # 'startxref' may be on the same line as the location + if not line.startswith(b"startxref"): + raise PdfReadError("startxref not found") + startxref = int(line[9:].strip()) + logger_warning("startxref on same line as offset", __name__) + else: + line = read_previous_line(stream) + if line[:9] != b"startxref": + raise PdfReadError("startxref not found") + return startxref + + def _read_standard_xref_table(self, stream: StreamType) -> None: + # standard cross-reference table + ref = stream.read(3) + if ref != b"ref": + raise PdfReadError("xref table read error") + read_non_whitespace(stream) + stream.seek(-1, 1) + first_time = True # check if the first time looking at the xref table + while True: + num = cast(int, read_object(stream, self)) + if first_time and num != 0: + self.xref_index = num + if self.strict: + logger_warning( + "Xref table not zero-indexed. ID numbers for objects will be corrected.", + __name__, + ) + # if table not zero indexed, could be due to error from when PDF was created + # which will lead to mismatched indices later on, only warned and corrected if self.strict==True + first_time = False + read_non_whitespace(stream) + stream.seek(-1, 1) + size = cast(int, read_object(stream, self)) + if not isinstance(size, int): + logger_warning( + "Invalid/Truncated xref table. Rebuilding it.", + __name__, + ) + self._rebuild_xref_table(stream) + stream.read() + return + read_non_whitespace(stream) + stream.seek(-1, 1) + cnt = 0 + while cnt < size: + line = stream.read(20) + + # It's very clear in section 3.4.3 of the PDF spec + # that all cross-reference table lines are a fixed + # 20 bytes (as of PDF 1.7). However, some files have + # 21-byte entries (or more) due to the use of \r\n + # (CRLF) EOL's. Detect that case, and adjust the line + # until it does not begin with a \r (CR) or \n (LF). + while line[0] in b"\x0D\x0A": + stream.seek(-20 + 1, 1) + line = stream.read(20) + + # On the other hand, some malformed PDF files + # use a single character EOL without a preceding + # space. Detect that case, and seek the stream + # back one character (0-9 means we've bled into + # the next xref entry, t means we've bled into the + # text "trailer"): + if line[-1] in b"0123456789t": + stream.seek(-1, 1) + + try: + offset_b, generation_b = line[:16].split(b" ") + entry_type_b = line[17:18] + + offset, generation = int(offset_b), int(generation_b) + except Exception: + # if something wrong occurred + if hasattr(stream, "getbuffer"): + buf = bytes(stream.getbuffer()) + else: + p = stream.tell() + stream.seek(0, 0) + buf = stream.read(-1) + stream.seek(p) + + f = re.search(f"{num}\\s+(\\d+)\\s+obj".encode(), buf) + if f is None: + logger_warning( + f"entry {num} in Xref table invalid; object not found", + __name__, + ) + generation = 65535 + offset = -1 + else: + logger_warning( + f"entry {num} in Xref table invalid but object found", + __name__, + ) + generation = int(f.group(1)) + offset = f.start() + + if generation not in self.xref: + self.xref[generation] = {} + self.xref_free_entry[generation] = {} + if num in self.xref[generation]: + # It really seems like we should allow the last + # xref table in the file to override previous + # ones. Since we read the file backwards, assume + # any existing key is already set correctly. + pass + else: + if entry_type_b == b"n": + self.xref[generation][num] = offset + try: + self.xref_free_entry[generation][num] = entry_type_b == b"f" + except Exception: + pass + try: + self.xref_free_entry[65535][num] = entry_type_b == b"f" + except Exception: + pass + cnt += 1 + num += 1 + read_non_whitespace(stream) + stream.seek(-1, 1) + trailer_tag = stream.read(7) + if trailer_tag != b"trailer": + # more xrefs! + stream.seek(-7, 1) + else: + break + + def _read_xref_tables_and_trailers( + self, stream: StreamType, startxref: Optional[int], xref_issue_nr: int + ) -> None: + self.xref = {} + self.xref_free_entry = {} + self.xref_objStm = {} + self.trailer = DictionaryObject() + while startxref is not None: + # load the xref table + stream.seek(startxref, 0) + x = stream.read(1) + if x in b"\r\n": + x = stream.read(1) + if x == b"x": + startxref = self._read_xref(stream) + elif xref_issue_nr: + try: + self._rebuild_xref_table(stream) + break + except Exception: + xref_issue_nr = 0 + elif x.isdigit(): + try: + xrefstream = self._read_pdf15_xref_stream(stream) + except Exception as e: + if TK.ROOT in self.trailer: + logger_warning( + f"Previous trailer can not be read {e.args}", + __name__, + ) + break + else: + raise PdfReadError(f"trailer can not be read {e.args}") + trailer_keys = TK.ROOT, TK.ENCRYPT, TK.INFO, TK.ID, TK.SIZE + for key in trailer_keys: + if key in xrefstream and key not in self.trailer: + self.trailer[NameObject(key)] = xrefstream.raw_get(key) + if "/XRefStm" in xrefstream: + p = stream.tell() + stream.seek(cast(int, xrefstream["/XRefStm"]) + 1, 0) + self._read_pdf15_xref_stream(stream) + stream.seek(p, 0) + if "/Prev" in xrefstream: + startxref = cast(int, xrefstream["/Prev"]) + else: + break + else: + startxref = self._read_xref_other_error(stream, startxref) + + def _read_xref(self, stream: StreamType) -> Optional[int]: + self._read_standard_xref_table(stream) + if stream.read(1) == b"": + return None + stream.seek(-1, 1) + read_non_whitespace(stream) + stream.seek(-1, 1) + new_trailer = cast(Dict[str, Any], read_object(stream, self)) + for key, value in new_trailer.items(): + if key not in self.trailer: + self.trailer[key] = value + if "/XRefStm" in new_trailer: + p = stream.tell() + stream.seek(cast(int, new_trailer["/XRefStm"]) + 1, 0) + try: + self._read_pdf15_xref_stream(stream) + except Exception: + logger_warning( + f"XRef object at {new_trailer['/XRefStm']} can not be read, some object may be missing", + __name__, + ) + stream.seek(p, 0) + if "/Prev" in new_trailer: + startxref = new_trailer["/Prev"] + return startxref + else: + return None + + def _read_xref_other_error( + self, stream: StreamType, startxref: int + ) -> Optional[int]: + # some PDFs have /Prev=0 in the trailer, instead of no /Prev + if startxref == 0: + if self.strict: + raise PdfReadError( + "/Prev=0 in the trailer (try opening with strict=False)" + ) + logger_warning( + "/Prev=0 in the trailer - assuming there is no previous xref table", + __name__, + ) + return None + # bad xref character at startxref. Let's see if we can find + # the xref table nearby, as we've observed this error with an + # off-by-one before. + stream.seek(-11, 1) + tmp = stream.read(20) + xref_loc = tmp.find(b"xref") + if xref_loc != -1: + startxref -= 10 - xref_loc + return startxref + # No explicit xref table, try finding a cross-reference stream. + stream.seek(startxref, 0) + for look in range(25): # value extended to cope with more linearized files + if stream.read(1).isdigit(): + # This is not a standard PDF, consider adding a warning + startxref += look + return startxref + # no xref table found at specified location + if "/Root" in self.trailer and not self.strict: + # if Root has been already found, just raise warning + logger_warning("Invalid parent xref., rebuild xref", __name__) + try: + self._rebuild_xref_table(stream) + return None + except Exception: + raise PdfReadError("can not rebuild xref") + raise PdfReadError("Could not find xref table at specified location") + + def _read_pdf15_xref_stream( + self, stream: StreamType + ) -> Union[ContentStream, EncodedStreamObject, DecodedStreamObject]: + # PDF 1.5+ Cross-Reference Stream + stream.seek(-1, 1) + idnum, generation = self.read_object_header(stream) + xrefstream = cast(ContentStream, read_object(stream, self)) + assert cast(str, xrefstream["/Type"]) == "/XRef" + self.cache_indirect_object(generation, idnum, xrefstream) + stream_data = BytesIO(xrefstream.get_data()) + # Index pairs specify the subsections in the dictionary. If + # none create one subsection that spans everything. + idx_pairs = xrefstream.get("/Index", [0, xrefstream.get("/Size")]) + entry_sizes = cast(Dict[Any, Any], xrefstream.get("/W")) + assert len(entry_sizes) >= 3 + if self.strict and len(entry_sizes) > 3: + raise PdfReadError(f"Too many entry sizes: {entry_sizes}") + + def get_entry(i: int) -> Union[int, Tuple[int, ...]]: + # Reads the correct number of bytes for each entry. See the + # discussion of the W parameter in PDF spec table 17. + if entry_sizes[i] > 0: + d = stream_data.read(entry_sizes[i]) + return convert_to_int(d, entry_sizes[i]) + + # PDF Spec Table 17: A value of zero for an element in the + # W array indicates...the default value shall be used + if i == 0: + return 1 # First value defaults to 1 + else: + return 0 + + def used_before(num: int, generation: Union[int, Tuple[int, ...]]) -> bool: + # We move backwards through the xrefs, don't replace any. + return num in self.xref.get(generation, []) or num in self.xref_objStm # type: ignore + + # Iterate through each subsection + self._read_xref_subsections(idx_pairs, get_entry, used_before) + return xrefstream + + @staticmethod + def _get_xref_issues(stream: StreamType, startxref: int) -> int: + """ + Return an int which indicates an issue. 0 means there is no issue. + + Args: + stream: + startxref: + + Returns: + 0 means no issue, other values represent specific issues. + """ + stream.seek(startxref - 1, 0) # -1 to check character before + line = stream.read(1) + if line == b"j": + line = stream.read(1) + if line not in b"\r\n \t": + return 1 + line = stream.read(4) + if line != b"xref": + # not an xref so check if it is an XREF object + line = b"" + while line in b"0123456789 \t": + line = stream.read(1) + if line == b"": + return 2 + line += stream.read(2) # 1 char already read, +2 to check "obj" + if line.lower() != b"obj": + return 3 + return 0 + + def _rebuild_xref_table(self, stream: StreamType) -> None: + self.xref = {} + stream.seek(0, 0) + f_ = stream.read(-1) + + for m in re.finditer(rb"[\r\n \t][ \t]*(\d+)[ \t]+(\d+)[ \t]+obj", f_): + idnum = int(m.group(1)) + generation = int(m.group(2)) + if generation not in self.xref: + self.xref[generation] = {} + self.xref[generation][idnum] = m.start(1) + + logger_warning("parsing for Object Streams", __name__) + for g in self.xref: + for i in self.xref[g]: + # get_object in manual + stream.seek(self.xref[g][i], 0) + try: + _ = self.read_object_header(stream) + o = cast(StreamObject, read_object(stream, self)) + if o.get("/Type", "") != "/ObjStm": + continue + strm = BytesIO(o.get_data()) + cpt = 0 + while True: + s = read_until_whitespace(strm) + if not s.isdigit(): + break + _i = int(s) + skip_over_whitespace(strm) + strm.seek(-1, 1) + s = read_until_whitespace(strm) + if not s.isdigit(): # pragma: no cover + break # pragma: no cover + _o = int(s) + self.xref_objStm[_i] = (i, _o) + cpt += 1 + if cpt != o.get("/N"): # pragma: no cover + logger_warning( # pragma: no cover + f"found {cpt} objects within Object({i},{g})" + f" whereas {o.get('/N')} expected", + __name__, + ) + except Exception: # could be of many cause + pass + + stream.seek(0, 0) + for m in re.finditer(rb"[\r\n \t][ \t]*trailer[\r\n \t]*(<<)", f_): + stream.seek(m.start(1), 0) + new_trailer = cast(Dict[Any, Any], read_object(stream, self)) + # Here, we are parsing the file from start to end, the new data have to erase the existing. + for key, value in list(new_trailer.items()): + self.trailer[key] = value + + def _read_xref_subsections( + self, + idx_pairs: List[int], + get_entry: Callable[[int], Union[int, Tuple[int, ...]]], + used_before: Callable[[int, Union[int, Tuple[int, ...]]], bool], + ) -> None: + for start, size in self._pairs(idx_pairs): + # The subsections must increase + for num in range(start, start + size): + # The first entry is the type + xref_type = get_entry(0) + # The rest of the elements depend on the xref_type + if xref_type == 0: + # linked list of free objects + next_free_object = get_entry(1) # noqa: F841 + next_generation = get_entry(2) # noqa: F841 + elif xref_type == 1: + # objects that are in use but are not compressed + byte_offset = get_entry(1) + generation = get_entry(2) + if generation not in self.xref: + self.xref[generation] = {} # type: ignore + if not used_before(num, generation): + self.xref[generation][num] = byte_offset # type: ignore + elif xref_type == 2: + # compressed objects + objstr_num = get_entry(1) + obstr_idx = get_entry(2) + generation = 0 # PDF spec table 18, generation is 0 + if not used_before(num, generation): + self.xref_objStm[num] = (objstr_num, obstr_idx) + elif self.strict: + raise PdfReadError(f"Unknown xref type: {xref_type}") + + def _pairs(self, array: List[int]) -> Iterable[Tuple[int, int]]: + i = 0 + while True: + yield array[i], array[i + 1] + i += 2 + if (i + 1) >= len(array): + break + + def decrypt(self, password: Union[str, bytes]) -> PasswordType: + """ + When using an encrypted / secured PDF file with the PDF Standard + encryption handler, this function will allow the file to be decrypted. + It checks the given password against the document's user password and + owner password, and then stores the resulting decryption key if either + password is correct. + + It does not matter which password was matched. Both passwords provide + the correct decryption key that will allow the document to be used with + this library. + + Args: + password: The password to match. + + Returns: + An indicator if the document was decrypted and whether it was the + owner password or the user password. + """ + if not self._encryption: + raise PdfReadError("Not encrypted file") + # TODO: raise Exception for wrong password + return self._encryption.verify(password) + + @property + def is_encrypted(self) -> bool: + """ + Read-only boolean property showing whether this PDF file is encrypted. + + Note that this property, if true, will remain true even after the + :meth:`decrypt()` method is called. + """ + return TK.ENCRYPT in self.trailer + + def add_form_topname(self, name: str) -> Optional[DictionaryObject]: + """ + Add a top level form that groups all form fields below it. + + Args: + name: text string of the "/T" Attribute of the created object + + Returns: + The created object. ``None`` means no object was created. + """ + catalog = self.root_object + + if "/AcroForm" not in catalog or not isinstance( + catalog["/AcroForm"], DictionaryObject + ): + return None + acroform = cast(DictionaryObject, catalog[NameObject("/AcroForm")]) + if "/Fields" not in acroform: + # TODO: :No error returns but may be extended for XFA Forms + return None + + interim = DictionaryObject() + interim[NameObject("/T")] = TextStringObject(name) + interim[NameObject("/Kids")] = acroform[NameObject("/Fields")] + self.cache_indirect_object( + 0, + max([i for (g, i) in self.resolved_objects if g == 0]) + 1, + interim, + ) + arr = ArrayObject() + arr.append(interim.indirect_reference) + acroform[NameObject("/Fields")] = arr + for o in cast(ArrayObject, interim["/Kids"]): + obj = o.get_object() + if "/Parent" in obj: + logger_warning( + f"Top Level Form Field {obj.indirect_reference} have a non-expected parent", + __name__, + ) + obj[NameObject("/Parent")] = interim.indirect_reference + return interim + + def rename_form_topname(self, name: str) -> Optional[DictionaryObject]: + """ + Rename top level form field that all form fields below it. + + Args: + name: text string of the "/T" field of the created object + + Returns: + The modified object. ``None`` means no object was modified. + """ + catalog = self.root_object + + if "/AcroForm" not in catalog or not isinstance( + catalog["/AcroForm"], DictionaryObject + ): + return None + acroform = cast(DictionaryObject, catalog[NameObject("/AcroForm")]) + if "/Fields" not in acroform: + return None + + interim = cast( + DictionaryObject, + cast(ArrayObject, acroform[NameObject("/Fields")])[0].get_object(), + ) + interim[NameObject("/T")] = TextStringObject(name) + return interim diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/__init__.py new file mode 100644 index 00000000..3b1d687e --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/__init__.py @@ -0,0 +1,285 @@ +""" +Code related to text extraction. + +Some parts are still in _page.py. In doubt, they will stay there. +""" + +import math +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from ..generic import DictionaryObject, TextStringObject, encode_pdfdocencoding + +CUSTOM_RTL_MIN: int = -1 +CUSTOM_RTL_MAX: int = -1 +CUSTOM_RTL_SPECIAL_CHARS: List[int] = [] +LAYOUT_NEW_BT_GROUP_SPACE_WIDTHS: int = 5 + + +class OrientationNotFoundError(Exception): + pass + + +def set_custom_rtl( + _min: Union[str, int, None] = None, + _max: Union[str, int, None] = None, + specials: Union[str, List[int], None] = None, +) -> Tuple[int, int, List[int]]: + """ + Change the Right-To-Left and special characters custom parameters. + + Args: + _min: The new minimum value for the range of custom characters that + will be written right to left. + If set to ``None``, the value will not be changed. + If set to an integer or string, it will be converted to its ASCII code. + The default value is -1, which sets no additional range to be converted. + _max: The new maximum value for the range of custom characters that will + be written right to left. + If set to ``None``, the value will not be changed. + If set to an integer or string, it will be converted to its ASCII code. + The default value is -1, which sets no additional range to be converted. + specials: The new list of special characters to be inserted in the + current insertion order. + If set to ``None``, the current value will not be changed. + If set to a string, it will be converted to a list of ASCII codes. + The default value is an empty list. + + Returns: + A tuple containing the new values for ``CUSTOM_RTL_MIN``, + ``CUSTOM_RTL_MAX``, and ``CUSTOM_RTL_SPECIAL_CHARS``. + """ + global CUSTOM_RTL_MIN, CUSTOM_RTL_MAX, CUSTOM_RTL_SPECIAL_CHARS + if isinstance(_min, int): + CUSTOM_RTL_MIN = _min + elif isinstance(_min, str): + CUSTOM_RTL_MIN = ord(_min) + if isinstance(_max, int): + CUSTOM_RTL_MAX = _max + elif isinstance(_max, str): + CUSTOM_RTL_MAX = ord(_max) + if isinstance(specials, str): + CUSTOM_RTL_SPECIAL_CHARS = [ord(x) for x in specials] + elif isinstance(specials, list): + CUSTOM_RTL_SPECIAL_CHARS = specials + return CUSTOM_RTL_MIN, CUSTOM_RTL_MAX, CUSTOM_RTL_SPECIAL_CHARS + + +def mult(m: List[float], n: List[float]) -> List[float]: + return [ + m[0] * n[0] + m[1] * n[2], + m[0] * n[1] + m[1] * n[3], + m[2] * n[0] + m[3] * n[2], + m[2] * n[1] + m[3] * n[3], + m[4] * n[0] + m[5] * n[2] + n[4], + m[4] * n[1] + m[5] * n[3] + n[5], + ] + + +def orient(m: List[float]) -> int: + if m[3] > 1e-6: + return 0 + elif m[3] < -1e-6: + return 180 + elif m[1] > 0: + return 90 + else: + return 270 + + +def crlf_space_check( + text: str, + cmtm_prev: Tuple[List[float], List[float]], + cmtm_matrix: Tuple[List[float], List[float]], + memo_cmtm: Tuple[List[float], List[float]], + cmap: Tuple[ + Union[str, Dict[int, str]], Dict[str, str], str, Optional[DictionaryObject] + ], + orientations: Tuple[int, ...], + output: str, + font_size: float, + visitor_text: Optional[Callable[[Any, Any, Any, Any, Any], None]], + spacewidth: float, +) -> Tuple[str, str, List[float], List[float]]: + cm_prev = cmtm_prev[0] + tm_prev = cmtm_prev[1] + cm_matrix = cmtm_matrix[0] + tm_matrix = cmtm_matrix[1] + memo_cm = memo_cmtm[0] + memo_tm = memo_cmtm[1] + + m_prev = mult(tm_prev, cm_prev) + m = mult(tm_matrix, cm_matrix) + orientation = orient(m) + delta_x = m[4] - m_prev[4] + delta_y = m[5] - m_prev[5] + k = math.sqrt(abs(m[0] * m[3]) + abs(m[1] * m[2])) + f = font_size * k + cm_prev = m + if orientation not in orientations: + raise OrientationNotFoundError + try: + if orientation == 0: + if delta_y < -0.8 * f: + if (output + text)[-1] != "\n": + output += text + "\n" + if visitor_text is not None: + visitor_text( + text + "\n", + memo_cm, + memo_tm, + cmap[3], + font_size, + ) + text = "" + elif ( + abs(delta_y) < f * 0.3 + and abs(delta_x) > spacewidth * f * 15 + and (output + text)[-1] != " " + ): + text += " " + elif orientation == 180: + if delta_y > 0.8 * f: + if (output + text)[-1] != "\n": + output += text + "\n" + if visitor_text is not None: + visitor_text( + text + "\n", + memo_cm, + memo_tm, + cmap[3], + font_size, + ) + text = "" + elif ( + abs(delta_y) < f * 0.3 + and abs(delta_x) > spacewidth * f * 15 + and (output + text)[-1] != " " + ): + text += " " + elif orientation == 90: + if delta_x > 0.8 * f: + if (output + text)[-1] != "\n": + output += text + "\n" + if visitor_text is not None: + visitor_text( + text + "\n", + memo_cm, + memo_tm, + cmap[3], + font_size, + ) + text = "" + elif ( + abs(delta_x) < f * 0.3 + and abs(delta_y) > spacewidth * f * 15 + and (output + text)[-1] != " " + ): + text += " " + elif orientation == 270: + if delta_x < -0.8 * f: + if (output + text)[-1] != "\n": + output += text + "\n" + if visitor_text is not None: + visitor_text( + text + "\n", + memo_cm, + memo_tm, + cmap[3], + font_size, + ) + text = "" + elif ( + abs(delta_x) < f * 0.3 + and abs(delta_y) > spacewidth * f * 15 + and (output + text)[-1] != " " + ): + text += " " + except Exception: + pass + tm_prev = tm_matrix.copy() + cm_prev = cm_matrix.copy() + return text, output, cm_prev, tm_prev + + +def handle_tj( + text: str, + operands: List[Union[str, TextStringObject]], + cm_matrix: List[float], + tm_matrix: List[float], + cmap: Tuple[ + Union[str, Dict[int, str]], Dict[str, str], str, Optional[DictionaryObject] + ], + orientations: Tuple[int, ...], + output: str, + font_size: float, + rtl_dir: bool, + visitor_text: Optional[Callable[[Any, Any, Any, Any, Any], None]], +) -> Tuple[str, bool]: + m = mult(tm_matrix, cm_matrix) + orientation = orient(m) + if orientation in orientations and len(operands) > 0: + if isinstance(operands[0], str): + text += operands[0] + else: + t: str = "" + tt: bytes = ( + encode_pdfdocencoding(operands[0]) + if isinstance(operands[0], str) + else operands[0] + ) + if isinstance(cmap[0], str): + try: + t = tt.decode(cmap[0], "surrogatepass") # apply str encoding + except Exception: + # the data does not match the expectation, + # we use the alternative ; + # text extraction may not be good + t = tt.decode( + "utf-16-be" if cmap[0] == "charmap" else "charmap", + "surrogatepass", + ) # apply str encoding + else: # apply dict encoding + t = "".join( + [cmap[0][x] if x in cmap[0] else bytes((x,)).decode() for x in tt] + ) + # "\u0590 - \u08FF \uFB50 - \uFDFF" + for x in [cmap[1][x] if x in cmap[1] else x for x in t]: + # x can be a sequence of bytes ; ex: habibi.pdf + if len(x) == 1: + xx = ord(x) + else: + xx = 1 + # fmt: off + if ( + # cases where the current inserting order is kept + (xx <= 0x2F) # punctuations but... + or 0x3A <= xx <= 0x40 # numbers (x30-39) + or 0x2000 <= xx <= 0x206F # upper punctuations.. + or 0x20A0 <= xx <= 0x21FF # but (numbers) indices/exponents + or xx in CUSTOM_RTL_SPECIAL_CHARS # customized.... + ): + text = x + text if rtl_dir else text + x + elif ( # right-to-left characters set + 0x0590 <= xx <= 0x08FF + or 0xFB1D <= xx <= 0xFDFF + or 0xFE70 <= xx <= 0xFEFF + or CUSTOM_RTL_MIN <= xx <= CUSTOM_RTL_MAX + ): + if not rtl_dir: + rtl_dir = True + output += text + if visitor_text is not None: + visitor_text(text, cm_matrix, tm_matrix, cmap[3], font_size) + text = "" + text = x + text + else: # left-to-right + # print(">",xx,x,end="") + if rtl_dir: + rtl_dir = False + output += text + if visitor_text is not None: + visitor_text(text, cm_matrix, tm_matrix, cmap[3], font_size) + text = "" + text = text + x + # fmt: on + return text, rtl_dir diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..b39cf9b0 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__init__.py new file mode 100644 index 00000000..8f4d5929 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__init__.py @@ -0,0 +1,16 @@ +"""Layout mode text extraction extension for pypdf""" +from ._fixed_width_page import ( + fixed_char_width, + fixed_width_page, + text_show_operations, + y_coordinate_groups, +) +from ._font import Font + +__all__ = [ + "fixed_char_width", + "fixed_width_page", + "text_show_operations", + "y_coordinate_groups", + "Font", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..6a746d9e Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_fixed_width_page.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_fixed_width_page.cpython-38.pyc new file mode 100644 index 00000000..362fd166 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_fixed_width_page.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_font.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_font.cpython-38.pyc new file mode 100644 index 00000000..51e844e2 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_font.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_font_widths.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_font_widths.cpython-38.pyc new file mode 100644 index 00000000..009f62be Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_font_widths.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_manager.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_manager.cpython-38.pyc new file mode 100644 index 00000000..19376035 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_manager.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_params.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_params.cpython-38.pyc new file mode 100644 index 00000000..3a6ab83f Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/__pycache__/_text_state_params.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_fixed_width_page.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_fixed_width_page.py new file mode 100644 index 00000000..e7af1b23 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_fixed_width_page.py @@ -0,0 +1,375 @@ +"""Extract PDF text preserving the layout of the source PDF""" + +from itertools import groupby +from math import ceil +from pathlib import Path +from typing import Any, Dict, Iterator, List, Literal, Optional, Tuple, TypedDict + +from ..._utils import logger_warning +from .. import LAYOUT_NEW_BT_GROUP_SPACE_WIDTHS +from ._font import Font +from ._text_state_manager import TextStateManager +from ._text_state_params import TextStateParams + + +class BTGroup(TypedDict): + """ + Dict describing a line of text rendered within a BT/ET operator pair. + If multiple text show operations render text on the same line, the text + will be combined into a single BTGroup dict. + + Keys: + tx: x coordinate of first character in BTGroup + ty: y coordinate of first character in BTGroup + font_size: nominal font size + font_height: effective font height + text: rendered text + displaced_tx: x coordinate of last character in BTGroup + flip_sort: -1 if page is upside down, else 1 + """ + + tx: float + ty: float + font_size: float + font_height: float + text: str + displaced_tx: float + flip_sort: Literal[-1, 1] + + +def bt_group(tj_op: TextStateParams, rendered_text: str, dispaced_tx: float) -> BTGroup: + """ + BTGroup constructed from a TextStateParams instance, rendered text, and + displaced tx value. + + Args: + tj_op (TextStateParams): TextStateParams instance + rendered_text (str): rendered text + dispaced_tx (float): x coordinate of last character in BTGroup + """ + return BTGroup( + tx=tj_op.tx, + ty=tj_op.ty, + font_size=tj_op.font_size, + font_height=tj_op.font_height, + text=rendered_text, + displaced_tx=dispaced_tx, + flip_sort=-1 if tj_op.flip_vertical else 1, + ) + + +def recurs_to_target_op( + ops: Iterator[Tuple[List[Any], bytes]], + text_state_mgr: TextStateManager, + end_target: Literal[b"Q", b"ET"], + fonts: Dict[str, Font], + strip_rotated: bool = True, +) -> Tuple[List[BTGroup], List[TextStateParams]]: + """ + Recurse operators between BT/ET and/or q/Q operators managing the transform + stack and capturing text positioning and rendering data. + + Args: + ops: iterator of operators in content stream + text_state_mgr: a TextStateManager instance + end_target: Either b"Q" (ends b"q" op) or b"ET" (ends b"BT" op) + fonts: font dictionary as returned by PageObject._layout_mode_fonts() + + Returns: + tuple: list of BTGroup dicts + list of TextStateParams dataclass instances. + """ + # 1 entry per line of text rendered within each BT/ET operation. + bt_groups: List[BTGroup] = [] + + # 1 entry per text show operator (Tj/TJ/'/") + tj_ops: List[TextStateParams] = [] + + if end_target == b"Q": + # add new q level. cm's added at this level will be popped at next b'Q' + text_state_mgr.add_q() + + while True: + try: + operands, op = next(ops) + except StopIteration: + return bt_groups, tj_ops + if op == end_target: + if op == b"Q": + text_state_mgr.remove_q() + if op == b"ET": + if not tj_ops: + return bt_groups, tj_ops + _text = "" + bt_idx = 0 # idx of first tj in this bt group + last_displaced_tx = tj_ops[bt_idx].displaced_tx + last_ty = tj_ops[bt_idx].ty + for _idx, _tj in enumerate( + tj_ops + ): # ... build text from new Tj operators + if strip_rotated and _tj.rotated: + continue + # if the y position of the text is greater than the font height, assume + # the text is on a new line and start a new group + if abs(_tj.ty - last_ty) > _tj.font_height: + if _text.strip(): + bt_groups.append( + bt_group(tj_ops[bt_idx], _text, last_displaced_tx) + ) + bt_idx = _idx + _text = "" + + # if the x position of the text is less than the last x position by + # more than 5 spaces widths, assume the text order should be flipped + # and start a new group + if ( + last_displaced_tx - _tj.tx + > _tj.space_tx * LAYOUT_NEW_BT_GROUP_SPACE_WIDTHS + ): + if _text.strip(): + bt_groups.append( + bt_group(tj_ops[bt_idx], _text, last_displaced_tx) + ) + bt_idx = _idx + last_displaced_tx = _tj.displaced_tx + _text = "" + + # calculate excess x translation based on ending tx of previous Tj. + # multiply by bool (_idx != bt_idx) to ensure spaces aren't double + # applied to the first tj of a BTGroup in fixed_width_page(). + excess_tx = round(_tj.tx - last_displaced_tx, 3) * (_idx != bt_idx) + # space_tx could be 0 if either Tz or font_size was 0 for this _tj. + spaces = int(excess_tx // _tj.space_tx) if _tj.space_tx else 0 + new_text = f'{" " * spaces}{_tj.txt}' + + last_ty = _tj.ty + _text = f"{_text}{new_text}" + last_displaced_tx = _tj.displaced_tx + if _text: + bt_groups.append(bt_group(tj_ops[bt_idx], _text, last_displaced_tx)) + text_state_mgr.reset_tm() + return bt_groups, tj_ops + if op == b"q": + bts, tjs = recurs_to_target_op( + ops, text_state_mgr, b"Q", fonts, strip_rotated + ) + bt_groups.extend(bts) + tj_ops.extend(tjs) + elif op == b"cm": + text_state_mgr.add_cm(*operands) + elif op == b"BT": + bts, tjs = recurs_to_target_op( + ops, text_state_mgr, b"ET", fonts, strip_rotated + ) + bt_groups.extend(bts) + tj_ops.extend(tjs) + elif op == b"Tj": + tj_ops.append(text_state_mgr.text_state_params(operands[0])) + elif op == b"TJ": + _tj = text_state_mgr.text_state_params() + for tj_op in operands[0]: + if isinstance(tj_op, bytes): + _tj = text_state_mgr.text_state_params(tj_op) + tj_ops.append(_tj) + else: + text_state_mgr.add_trm(_tj.displacement_matrix(TD_offset=tj_op)) + elif op == b"'": + text_state_mgr.reset_trm() + text_state_mgr.add_tm([0, -text_state_mgr.TL]) + tj_ops.append(text_state_mgr.text_state_params(operands[0])) + elif op == b'"': + text_state_mgr.reset_trm() + text_state_mgr.set_state_param(b"Tw", operands[0]) + text_state_mgr.set_state_param(b"Tc", operands[1]) + text_state_mgr.add_tm([0, -text_state_mgr.TL]) + tj_ops.append(text_state_mgr.text_state_params(operands[2])) + elif op in (b"Td", b"Tm", b"TD", b"T*"): + text_state_mgr.reset_trm() + if op == b"Tm": + text_state_mgr.reset_tm() + elif op == b"TD": + text_state_mgr.set_state_param(b"TL", -operands[1]) + elif op == b"T*": + operands = [0, -text_state_mgr.TL] + text_state_mgr.add_tm(operands) + elif op == b"Tf": + text_state_mgr.set_font(fonts[operands[0]], operands[1]) + else: # handle Tc, Tw, Tz, TL, and Ts operators + text_state_mgr.set_state_param(op, operands) + + +def y_coordinate_groups( + bt_groups: List[BTGroup], debug_path: Optional[Path] = None +) -> Dict[int, List[BTGroup]]: + """ + Group text operations by rendered y coordinate, i.e. the line number. + + Args: + bt_groups: list of dicts as returned by text_show_operations() + debug_path (Path, optional): Path to a directory for saving debug output. + + Returns: + Dict[int, List[BTGroup]]: dict of lists of text rendered by each BT operator + keyed by y coordinate + """ + ty_groups = { + ty: sorted(grp, key=lambda x: x["tx"]) + for ty, grp in groupby( + bt_groups, key=lambda bt_grp: int(bt_grp["ty"] * bt_grp["flip_sort"]) + ) + } + # combine groups whose y coordinates differ by less than the effective font height + # (accounts for mixed fonts and other minor oddities) + last_ty = next(iter(ty_groups)) + last_txs = {int(_t["tx"]) for _t in ty_groups[last_ty] if _t["text"].strip()} + for ty in list(ty_groups)[1:]: + fsz = min(ty_groups[_y][0]["font_height"] for _y in (ty, last_ty)) + txs = {int(_t["tx"]) for _t in ty_groups[ty] if _t["text"].strip()} + # prevent merge if both groups are rendering in the same x position. + no_text_overlap = not (txs & last_txs) + offset_less_than_font_height = abs(ty - last_ty) < fsz + if no_text_overlap and offset_less_than_font_height: + ty_groups[last_ty] = sorted( + ty_groups.pop(ty) + ty_groups[last_ty], key=lambda x: x["tx"] + ) + last_txs |= txs + else: + last_ty = ty + last_txs = txs + if debug_path: # pragma: no cover + import json + + debug_path.joinpath("bt_groups.json").write_text( + json.dumps(ty_groups, indent=2, default=str), "utf-8" + ) + return ty_groups + + +def text_show_operations( + ops: Iterator[Tuple[List[Any], bytes]], + fonts: Dict[str, Font], + strip_rotated: bool = True, + debug_path: Optional[Path] = None, +) -> List[BTGroup]: + """ + Extract text from BT/ET operator pairs. + + Args: + ops (Iterator[Tuple[List, bytes]]): iterator of operators in content stream + fonts (Dict[str, Font]): font dictionary + strip_rotated: Removes text if rotated w.r.t. to the page. Defaults to True. + debug_path (Path, optional): Path to a directory for saving debug output. + + Returns: + List[BTGroup]: list of dicts of text rendered by each BT operator + """ + state_mgr = TextStateManager() # transformation stack manager + debug = bool(debug_path) + bt_groups: List[BTGroup] = [] # BT operator dict + tj_debug: List[TextStateParams] = [] # Tj/TJ operator data (debug only) + try: + warned_rotation = False + while True: + operands, op = next(ops) + if op in (b"BT", b"q"): + bts, tjs = recurs_to_target_op( + ops, state_mgr, b"ET" if op == b"BT" else b"Q", fonts, strip_rotated + ) + if not warned_rotation and any(tj.rotated for tj in tjs): + warned_rotation = True + if strip_rotated: + logger_warning( + "Rotated text discovered. Output will be incomplete.", + __name__, + ) + else: + logger_warning( + "Rotated text discovered. Layout will be degraded.", + __name__, + ) + bt_groups.extend(bts) + if debug: # pragma: no cover + tj_debug.extend(tjs) + else: # set Tc, Tw, Tz, TL, and Ts if required. ignores all other ops + state_mgr.set_state_param(op, operands) + except StopIteration: + pass + + # left align the data, i.e. decrement all tx values by min(tx) + min_x = min((x["tx"] for x in bt_groups), default=0.0) + bt_groups = [ + dict(ogrp, tx=ogrp["tx"] - min_x, displaced_tx=ogrp["displaced_tx"] - min_x) # type: ignore[misc] + for ogrp in sorted( + bt_groups, key=lambda x: (x["ty"] * x["flip_sort"], -x["tx"]), reverse=True + ) + ] + + if debug_path: # pragma: no cover + import json + + debug_path.joinpath("bts.json").write_text( + json.dumps(bt_groups, indent=2, default=str), "utf-8" + ) + debug_path.joinpath("tjs.json").write_text( + json.dumps( + tj_debug, indent=2, default=lambda x: getattr(x, "to_dict", str)(x) + ), + "utf-8", + ) + return bt_groups + + +def fixed_char_width(bt_groups: List[BTGroup], scale_weight: float = 1.25) -> float: + """ + Calculate average character width weighted by the length of the rendered + text in each sample for conversion to fixed-width layout. + + Args: + bt_groups (List[BTGroup]): List of dicts of text rendered by each + BT operator + + Returns: + float: fixed character width + """ + char_widths = [] + for _bt in bt_groups: + _len = len(_bt["text"]) * scale_weight + char_widths.append(((_bt["displaced_tx"] - _bt["tx"]) / _len, _len)) + return sum(_w * _l for _w, _l in char_widths) / sum(_l for _, _l in char_widths) + + +def fixed_width_page( + ty_groups: Dict[int, List[BTGroup]], char_width: float, space_vertically: bool +) -> str: + """ + Generate page text from text operations grouped by rendered y coordinate. + + Args: + ty_groups: dict of text show ops as returned by y_coordinate_groups() + char_width: fixed character width + space_vertically: include blank lines inferred from y distance + font height. + + Returns: + str: page text in a fixed width format that closely adheres to the rendered + layout in the source pdf. + """ + lines: List[str] = [] + last_y_coord = 0 + for y_coord, line_data in ty_groups.items(): + if space_vertically and lines: + blank_lines = ( + int(abs(y_coord - last_y_coord) / line_data[0]["font_height"]) - 1 + ) + lines.extend([""] * blank_lines) + line = "" + last_disp = 0.0 + for bt_op in line_data: + offset = int(bt_op["tx"] // char_width) + spaces = (offset - len(line)) * (ceil(last_disp) < int(bt_op["tx"])) + line = f"{line}{' ' * spaces}{bt_op['text']}" + last_disp = bt_op["displaced_tx"] + if line.strip() or lines: + lines.append( + "".join(c if ord(c) < 14 or ord(c) > 31 else " " for c in line) + ) + last_y_coord = y_coord + return "\n".join(ln.rstrip() for ln in lines if space_vertically or ln.strip()) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_font.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_font.py new file mode 100644 index 00000000..1d9617d7 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_font.py @@ -0,0 +1,132 @@ +"""Font constants and classes for "layout" mode text operations""" + +from dataclasses import dataclass, field +from typing import Any, Dict, Sequence, Union, cast + +from ...errors import ParseError +from ...generic import IndirectObject +from ._font_widths import STANDARD_WIDTHS + + +@dataclass +class Font: + """ + A font object formatted for use during "layout" mode text extraction + + Attributes: + subtype (str): font subtype + space_width (int | float): width of a space character + encoding (str | Dict[int, str]): font encoding + char_map (dict): character map + font_dictionary (dict): font dictionary + """ + + subtype: str + space_width: Union[int, float] + encoding: Union[str, Dict[int, str]] + char_map: Dict[Any, Any] + font_dictionary: Dict[Any, Any] + width_map: Dict[str, int] = field(default_factory=dict, init=False) + + def __post_init__(self) -> None: + # TrueType fonts have a /Widths array mapping character codes to widths + if isinstance(self.encoding, dict) and "/Widths" in self.font_dictionary: + first_char = self.font_dictionary.get("/FirstChar", 0) + self.width_map = { + self.encoding.get(idx + first_char, chr(idx + first_char)): width + for idx, width in enumerate(self.font_dictionary["/Widths"]) + } + + # CID fonts have a /W array mapping character codes to widths stashed in /DescendantFonts + if "/DescendantFonts" in self.font_dictionary: + d_font: Dict[Any, Any] + for d_font_idx, d_font in enumerate( + self.font_dictionary["/DescendantFonts"] + ): + while isinstance(d_font, IndirectObject): + d_font = d_font.get_object() + self.font_dictionary["/DescendantFonts"][d_font_idx] = d_font + ord_map = { + ord(_target): _surrogate + for _target, _surrogate in self.char_map.items() + if isinstance(_target, str) + } + # /W width definitions have two valid formats which can be mixed and matched: + # (1) A character start index followed by a list of widths, e.g. + # `45 [500 600 700]` applies widths 500, 600, 700 to characters 45-47. + # (2) A character start index, a character stop index, and a width, e.g. + # `45 65 500` applies width 500 to characters 45-65. + skip_count = 0 + _w = d_font.get("/W", []) + for idx, w_entry in enumerate(_w): + w_entry = w_entry.get_object() + if skip_count: + skip_count -= 1 + continue + if not isinstance(w_entry, (int, float)): # pragma: no cover + # We should never get here due to skip_count above. Add a + # warning and or use reader's "strict" to force an ex??? + continue + # check for format (1): `int [int int int int ...]` + w_next_entry = _w[idx + 1].get_object() + if isinstance(w_next_entry, Sequence): + start_idx, width_list = w_entry, w_next_entry + self.width_map.update( + { + ord_map[_cidx]: _width + for _cidx, _width in zip( + range( + cast(int, start_idx), + cast(int, start_idx) + len(width_list), + 1, + ), + width_list, + ) + if _cidx in ord_map + } + ) + skip_count = 1 + # check for format (2): `int int int` + elif isinstance(w_next_entry, (int, float)) and isinstance( + _w[idx + 2].get_object(), (int, float) + ): + start_idx, stop_idx, const_width = ( + w_entry, + w_next_entry, + _w[idx + 2].get_object(), + ) + self.width_map.update( + { + ord_map[_cidx]: const_width + for _cidx in range( + cast(int, start_idx), cast(int, stop_idx + 1), 1 + ) + if _cidx in ord_map + } + ) + skip_count = 2 + else: + # Note: this doesn't handle the case of out of bounds (reaching the end of the width definitions + # while expecting more elements). This raises an IndexError which is sufficient. + raise ParseError( + f"Invalid font width definition. Next elements: {w_entry}, {w_next_entry}, {_w[idx + 2]}" + ) # pragma: no cover + + if not self.width_map and "/BaseFont" in self.font_dictionary: + for key in STANDARD_WIDTHS: + if self.font_dictionary["/BaseFont"].startswith(f"/{key}"): + self.width_map = STANDARD_WIDTHS[key] + break + + def word_width(self, word: str) -> float: + """Sum of character widths specified in PDF font for the supplied word""" + return sum( + [self.width_map.get(char, self.space_width * 2) for char in word], 0.0 + ) + + @staticmethod + def to_dict(font_instance: "Font") -> Dict[str, Any]: + """Dataclass to dict for json.dumps serialization.""" + return { + k: getattr(font_instance, k) for k in font_instance.__dataclass_fields__ + } diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_font_widths.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_font_widths.py new file mode 100644 index 00000000..39092bcd --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_font_widths.py @@ -0,0 +1,208 @@ +# Widths for the standard 14 fonts as described on page 416 of the PDF 1.7 standard +STANDARD_WIDTHS = { + "Helvetica": { # 4 fonts, includes bold, oblique and boldoblique variants + " ": 278, + "!": 278, + '"': 355, + "#": 556, + "$": 556, + "%": 889, + "&": 667, + "'": 191, + "(": 333, + ")": 333, + "*": 389, + "+": 584, + ",": 278, + "-": 333, + ".": 278, + "/": 278, + "0": 556, + "1": 556, + "2": 556, + "3": 556, + "4": 556, + "5": 556, + "6": 556, + "7": 556, + "8": 556, + "9": 556, + ":": 278, + ";": 278, + "<": 584, + "=": 584, + ">": 584, + "?": 611, + "@": 975, + "A": 667, + "B": 667, + "C": 722, + "D": 722, + "E": 667, + "F": 611, + "G": 778, + "H": 722, + "I": 278, + "J": 500, + "K": 667, + "L": 556, + "M": 833, + "N": 722, + "O": 778, + "P": 667, + "Q": 944, + "R": 667, + "S": 667, + "T": 611, + "U": 278, + "V": 278, + "W": 584, + "X": 556, + "Y": 556, + "Z": 500, + "[": 556, + "\\": 556, + "]": 556, + "^": 278, + "_": 278, + "`": 278, + "a": 278, + "b": 278, + "c": 333, + "d": 556, + "e": 556, + "f": 556, + "g": 556, + "h": 556, + "i": 556, + "j": 556, + "k": 556, + "l": 556, + "m": 556, + "n": 278, + "o": 278, + "p": 556, + "q": 556, + "r": 500, + "s": 556, + "t": 556, + "u": 278, + "v": 500, + "w": 500, + "x": 222, + "y": 222, + "z": 556, + "{": 222, + "|": 833, + "}": 556, + "~": 556, + }, + "Times": { # 4 fonts, includes bold, oblique and boldoblique variants + " ": 250, + "!": 333, + '"': 408, + "#": 500, + "$": 500, + "%": 833, + "&": 778, + "'": 180, + "(": 333, + ")": 333, + "*": 500, + "+": 564, + ",": 250, + "-": 333, + ".": 250, + "/": 564, + "0": 500, + "1": 500, + "2": 500, + "3": 500, + "4": 500, + "5": 500, + "6": 500, + "7": 500, + "8": 500, + "9": 500, + ":": 278, + ";": 278, + "<": 564, + "=": 564, + ">": 564, + "?": 444, + "@": 921, + "A": 722, + "B": 667, + "C": 667, + "D": 722, + "E": 611, + "F": 556, + "G": 722, + "H": 722, + "I": 333, + "J": 389, + "K": 722, + "L": 611, + "M": 889, + "N": 722, + "O": 722, + "P": 556, + "Q": 722, + "R": 667, + "S": 556, + "T": 611, + "U": 722, + "V": 722, + "W": 944, + "X": 722, + "Y": 722, + "Z": 611, + "[": 333, + "\\": 278, + "]": 333, + "^": 469, + "_": 500, + "`": 333, + "a": 444, + "b": 500, + "c": 444, + "d": 500, + "e": 444, + "f": 333, + "g": 500, + "h": 500, + "i": 278, + "j": 278, + "k": 500, + "l": 278, + "m": 722, + "n": 500, + "o": 500, + "p": 500, + "q": 500, + "r": 333, + "s": 389, + "t": 278, + "u": 500, + "v": 444, + "w": 722, + "x": 500, + "y": 444, + "z": 389, + "{": 348, + "|": 220, + "}": 348, + "~": 469, + }, +} +STANDARD_WIDTHS[ + "Courier" +] = { # 4 fonts, includes bold, oblique and boldoblique variants + c: 600 for c in STANDARD_WIDTHS["Times"] # fixed width +} +STANDARD_WIDTHS["ZapfDingbats"] = {c: 1000 for c in STANDARD_WIDTHS["Times"]} # 1 font +STANDARD_WIDTHS["Symbol"] = {c: 500 for c in STANDARD_WIDTHS["Times"]} # 1 font +# add aliases per table H.3 on page 1110 of the PDF 1.7 standard +STANDARD_WIDTHS["CourierNew"] = STANDARD_WIDTHS["Courier"] +STANDARD_WIDTHS["Arial"] = STANDARD_WIDTHS["Helvetica"] +STANDARD_WIDTHS["TimesNewRoman"] = STANDARD_WIDTHS["Times"] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_text_state_manager.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_text_state_manager.py new file mode 100644 index 00000000..3c5d4736 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_text_state_manager.py @@ -0,0 +1,213 @@ +"""manage the PDF transform stack during "layout" mode text extraction""" + +from collections import ChainMap, Counter +from typing import Any, Dict, List, MutableMapping, Union +from typing import ChainMap as ChainMapType +from typing import Counter as CounterType + +from ...errors import PdfReadError +from .. import mult +from ._font import Font +from ._text_state_params import TextStateParams + +TextStateManagerChainMapType = ChainMapType[Union[int, str], Union[float, bool]] +TextStateManagerDictType = MutableMapping[Union[int, str], Union[float, bool]] + + +class TextStateManager: + """ + Tracks the current text state including cm/tm/trm transformation matrices. + + Attributes: + transform_stack (ChainMap): ChainMap of cm/tm transformation matrices + q_queue (Counter[int]): Counter of q operators + q_depth (List[int]): list of q operator nesting levels + Tc (float): character spacing + Tw (float): word spacing + Tz (int): horizontal scaling + TL (float): leading + Ts (float): text rise + font (Font): font object + font_size (int | float): font size + """ + + def __init__(self) -> None: + self.transform_stack: TextStateManagerChainMapType = ChainMap( + self.new_transform() + ) + self.q_queue: CounterType[int] = Counter() + self.q_depth = [0] + self.Tc: float = 0.0 + self.Tw: float = 0.0 + self.Tz: float = 100.0 + self.TL: float = 0.0 + self.Ts: float = 0.0 + self.font: Union[Font, None] = None + self.font_size: Union[int, float] = 0 + + def set_state_param(self, op: bytes, value: Union[float, List[Any]]) -> None: + """ + Set a text state parameter. Supports Tc, Tz, Tw, TL, and Ts operators. + + Args: + op: operator read from PDF stream as bytes. No action is taken + for unsupported operators (see supported operators above). + value (float | List[Any]): new parameter value. If a list, + value[0] is used. + """ + if op not in [b"Tc", b"Tz", b"Tw", b"TL", b"Ts"]: + return + self.__setattr__(op.decode(), value[0] if isinstance(value, list) else value) + + def set_font(self, font: Font, size: float) -> None: + """ + Set the current font and font_size. + + Args: + font (Font): a layout mode Font + size (float): font size + """ + self.font = font + self.font_size = size + + def text_state_params(self, value: Union[bytes, str] = "") -> TextStateParams: + """ + Create a TextStateParams instance to display a text string. Type[bytes] values + will be decoded implicitly. + + Args: + value (str | bytes): text to associate with the captured state. + + Raises: + PdfReadError: if font not set (no Tf operator in incoming pdf content stream) + + Returns: + TextStateParams: current text state parameters + """ + if not isinstance(self.font, Font): + raise PdfReadError( + "font not set: is PDF missing a Tf operator?" + ) # pragma: no cover + if isinstance(value, bytes): + try: + if isinstance(self.font.encoding, str): + txt = value.decode(self.font.encoding, "surrogatepass") + else: + txt = "".join( + self.font.encoding[x] + if x in self.font.encoding + else bytes((x,)).decode() + for x in value + ) + except (UnicodeEncodeError, UnicodeDecodeError): + txt = value.decode("utf-8", "replace") + txt = "".join( + self.font.char_map[x] if x in self.font.char_map else x for x in txt + ) + else: + txt = value + return TextStateParams( + txt, + self.font, + self.font_size, + self.Tc, + self.Tw, + self.Tz, + self.TL, + self.Ts, + self.effective_transform, + ) + + @staticmethod + def raw_transform( + _a: float = 1.0, + _b: float = 0.0, + _c: float = 0.0, + _d: float = 1.0, + _e: float = 0.0, + _f: float = 0.0, + ) -> Dict[int, float]: + """Only a/b/c/d/e/f matrix params""" + return dict(zip(range(6), map(float, (_a, _b, _c, _d, _e, _f)))) + + @staticmethod + def new_transform( + _a: float = 1.0, + _b: float = 0.0, + _c: float = 0.0, + _d: float = 1.0, + _e: float = 0.0, + _f: float = 0.0, + is_text: bool = False, + is_render: bool = False, + ) -> TextStateManagerDictType: + """Standard a/b/c/d/e/f matrix params + 'is_text' and 'is_render' keys""" + result: Any = TextStateManager.raw_transform(_a, _b, _c, _d, _e, _f) + result.update({"is_text": is_text, "is_render": is_render}) + return result + + def reset_tm(self) -> TextStateManagerChainMapType: + """Clear all transforms from chainmap having is_text==True or is_render==True""" + while ( + self.transform_stack.maps[0]["is_text"] + or self.transform_stack.maps[0]["is_render"] + ): + self.transform_stack = self.transform_stack.parents + return self.transform_stack + + def reset_trm(self) -> TextStateManagerChainMapType: + """Clear all transforms from chainmap having is_render==True""" + while self.transform_stack.maps[0]["is_render"]: + self.transform_stack = self.transform_stack.parents + return self.transform_stack + + def remove_q(self) -> TextStateManagerChainMapType: + """Rewind to stack prior state after closing a 'q' with internal 'cm' ops""" + self.transform_stack = self.reset_tm() + self.transform_stack.maps = self.transform_stack.maps[ + self.q_queue.pop(self.q_depth.pop(), 0) : + ] + return self.transform_stack + + def add_q(self) -> None: + """Add another level to q_queue""" + self.q_depth.append(len(self.q_depth)) + + def add_cm(self, *args: Any) -> TextStateManagerChainMapType: + """Concatenate an additional transform matrix""" + self.transform_stack = self.reset_tm() + self.q_queue.update(self.q_depth[-1:]) + self.transform_stack = self.transform_stack.new_child(self.new_transform(*args)) + return self.transform_stack + + def _complete_matrix(self, operands: List[float]) -> List[float]: + """Adds a, b, c, and d to an "e/f only" operand set (e.g Td)""" + if len(operands) == 2: # this is a Td operator or equivalent + operands = [1.0, 0.0, 0.0, 1.0, *operands] + return operands + + def add_tm(self, operands: List[float]) -> TextStateManagerChainMapType: + """Append a text transform matrix""" + self.transform_stack = self.transform_stack.new_child( + self.new_transform( # type: ignore[misc] + *self._complete_matrix(operands), is_text=True # type: ignore[arg-type] + ) + ) + return self.transform_stack + + def add_trm(self, operands: List[float]) -> TextStateManagerChainMapType: + """Append a text rendering transform matrix""" + self.transform_stack = self.transform_stack.new_child( + self.new_transform( # type: ignore[misc] + *self._complete_matrix(operands), is_text=True, is_render=True # type: ignore[arg-type] + ) + ) + return self.transform_stack + + @property + def effective_transform(self) -> List[float]: + """Current effective transform accounting for cm, tm, and trm transforms""" + eff_transform = [*self.transform_stack.maps[0].values()] + for transform in self.transform_stack.maps[1:]: + eff_transform = mult(eff_transform, transform) # type: ignore[arg-type] # dict has int keys 0-5 + return eff_transform diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_text_state_params.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_text_state_params.py new file mode 100644 index 00000000..b6e6930c --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_text_extraction/_layout_mode/_text_state_params.py @@ -0,0 +1,127 @@ +"""A dataclass that captures the CTM and Text State for a tj operation""" + +import math +from dataclasses import dataclass, field +from typing import Any, Dict, List, Union + +from .. import mult, orient +from ._font import Font + + +@dataclass +class TextStateParams: + """ + Text state parameters and operator values for a single text value in a + TJ or Tj PDF operation. + + Attributes: + txt (str): the text to be rendered. + font (Font): font object + font_size (int | float): font size + Tc (float): character spacing. Defaults to 0.0. + Tw (float): word spacing. Defaults to 0.0. + Tz (float): horizontal scaling. Defaults to 100.0. + TL (float): leading, vertical displacement between text lines. Defaults to 0.0. + Ts (float): text rise. Used for super/subscripts. Defaults to 0.0. + transform (List[float]): effective transformation matrix. + tx (float): x cood of rendered text, i.e. self.transform[4] + ty (float): y cood of rendered text. May differ from self.transform[5] per self.Ts. + displaced_tx (float): x coord immediately following rendered text + space_tx (float): tx for a space character + font_height (float): effective font height accounting for CTM + flip_vertical (bool): True if y axis has been inverted (i.e. if self.transform[3] < 0.) + rotated (bool): True if the text orientation is rotated with respect to the page. + """ + + txt: str + font: Font + font_size: Union[int, float] + Tc: float = 0.0 + Tw: float = 0.0 + Tz: float = 100.0 + TL: float = 0.0 + Ts: float = 0.0 + transform: List[float] = field( + default_factory=lambda: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + ) + tx: float = field(default=0.0, init=False) + ty: float = field(default=0.0, init=False) + displaced_tx: float = field(default=0.0, init=False) + space_tx: float = field(default=0.0, init=False) + font_height: float = field(default=0.0, init=False) + flip_vertical: bool = field(default=False, init=False) + rotated: bool = field(default=False, init=False) + + def __post_init__(self) -> None: + if orient(self.transform) in (90, 270): + self.transform = mult( + [1.0, -self.transform[1], -self.transform[2], 1.0, 0.0, 0.0], + self.transform, + ) + self.rotated = True + # self.transform[0] AND self.transform[3] < 0 indicates true rotation. + # If only self.transform[3] < 0, the y coords are simply inverted. + if orient(self.transform) == 180 and self.transform[0] < -1e-6: + self.transform = mult([-1.0, 0.0, 0.0, -1.0, 0.0, 0.0], self.transform) + self.rotated = True + self.displaced_tx = self.displaced_transform()[4] + self.tx = self.transform[4] + self.ty = self.render_transform()[5] + self.space_tx = round(self.word_tx(" "), 3) + if self.space_tx < 1e-6: + # if the " " char is assigned 0 width (e.g. for fine tuned spacing + # with TJ int operators a la crazyones.pdf), calculate space_tx as + # a TD_offset of -2 * font.space_width where font.space_width is + # the space_width calculated in _cmap.py. + self.space_tx = round(self.word_tx("", self.font.space_width * -2), 3) + self.font_height = self.font_size * math.sqrt( + self.transform[1] ** 2 + self.transform[3] ** 2 + ) + # flip_vertical handles PDFs generated by Microsoft Word's "publish" command. + self.flip_vertical = self.transform[3] < -1e-6 # inverts y axis + + def font_size_matrix(self) -> List[float]: + """Font size matrix""" + return [ + self.font_size * (self.Tz / 100.0), + 0.0, + 0.0, + self.font_size, + 0.0, + self.Ts, + ] + + def displaced_transform(self) -> List[float]: + """Effective transform matrix after text has been rendered.""" + return mult(self.displacement_matrix(), self.transform) + + def render_transform(self) -> List[float]: + """Effective transform matrix accounting for font size, Tz, and Ts.""" + return mult(self.font_size_matrix(), self.transform) + + def displacement_matrix( + self, word: Union[str, None] = None, TD_offset: float = 0.0 + ) -> List[float]: + """ + Text displacement matrix + + Args: + word (str, optional): Defaults to None in which case self.txt displacement is + returned. + TD_offset (float, optional): translation applied by TD operator. Defaults to 0.0. + """ + word = word if word is not None else self.txt + return [1.0, 0.0, 0.0, 1.0, self.word_tx(word, TD_offset), 0.0] + + def word_tx(self, word: str, TD_offset: float = 0.0) -> float: + """Horizontal text displacement for any word according this text state""" + return ( + (self.font_size * ((self.font.word_width(word) - TD_offset) / 1000.0)) + + self.Tc + + word.count(" ") * self.Tw + ) * (self.Tz / 100.0) + + @staticmethod + def to_dict(inst: "TextStateParams") -> Dict[str, Any]: + """Dataclass to dict for json.dumps serialization""" + return {k: getattr(inst, k) for k in inst.__dataclass_fields__ if k != "font"} diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_utils.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_utils.py new file mode 100644 index 00000000..e0034ccc --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_utils.py @@ -0,0 +1,593 @@ +# Copyright (c) 2006, Mathieu Fenniak +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +"""Utility functions for PDF library.""" +__author__ = "Mathieu Fenniak" +__author_email__ = "biziqe@mathieu.fenniak.net" + +import functools +import logging +import re +import sys +import warnings +from dataclasses import dataclass +from datetime import datetime, timezone +from io import DEFAULT_BUFFER_SIZE +from os import SEEK_CUR +from typing import ( + IO, + Any, + Dict, + List, + Optional, + Pattern, + Tuple, + Union, + overload, +) + +if sys.version_info[:2] >= (3, 10): + # Python 3.10+: https://www.python.org/dev/peps/pep-0484/ + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +from .errors import ( + STREAM_TRUNCATED_PREMATURELY, + DeprecationError, + PdfStreamError, +) + +TransformationMatrixType: TypeAlias = Tuple[ + Tuple[float, float, float], Tuple[float, float, float], Tuple[float, float, float] +] +CompressedTransformationMatrix: TypeAlias = Tuple[ + float, float, float, float, float, float +] + +StreamType = IO[Any] +StrByteType = Union[str, StreamType] + + +def parse_iso8824_date(text: Optional[str]) -> Optional[datetime]: + orgtext = text + if text is None: + return None + if text[0].isdigit(): + text = "D:" + text + if text.endswith(("Z", "z")): + text += "0000" + text = text.replace("z", "+").replace("Z", "+").replace("'", "") + i = max(text.find("+"), text.find("-")) + if i > 0 and i != len(text) - 5: + text += "00" + for f in ( + "D:%Y", + "D:%Y%m", + "D:%Y%m%d", + "D:%Y%m%d%H", + "D:%Y%m%d%H%M", + "D:%Y%m%d%H%M%S", + "D:%Y%m%d%H%M%S%z", + ): + try: + d = datetime.strptime(text, f) # noqa: DTZ007 + except ValueError: + continue + else: + if text.endswith("+0000"): + d = d.replace(tzinfo=timezone.utc) + return d + raise ValueError(f"Can not convert date: {orgtext}") + + +def _get_max_pdf_version_header(header1: str, header2: str) -> str: + versions = ( + "%PDF-1.3", + "%PDF-1.4", + "%PDF-1.5", + "%PDF-1.6", + "%PDF-1.7", + "%PDF-2.0", + ) + pdf_header_indices = [] + if header1 in versions: + pdf_header_indices.append(versions.index(header1)) + if header2 in versions: + pdf_header_indices.append(versions.index(header2)) + if len(pdf_header_indices) == 0: + raise ValueError(f"neither {header1!r} nor {header2!r} are proper headers") + return versions[max(pdf_header_indices)] + + +def read_until_whitespace(stream: StreamType, maxchars: Optional[int] = None) -> bytes: + """ + Read non-whitespace characters and return them. + + Stops upon encountering whitespace or when maxchars is reached. + + Args: + stream: The data stream from which was read. + maxchars: The maximum number of bytes returned; by default unlimited. + + Returns: + The data which was read. + """ + txt = b"" + while True: + tok = stream.read(1) + if tok.isspace() or not tok: + break + txt += tok + if len(txt) == maxchars: + break + return txt + + +def read_non_whitespace(stream: StreamType) -> bytes: + """ + Find and read the next non-whitespace character (ignores whitespace). + + Args: + stream: The data stream from which was read. + + Returns: + The data which was read. + """ + tok = stream.read(1) + while tok in WHITESPACES: + tok = stream.read(1) + return tok + + +def skip_over_whitespace(stream: StreamType) -> bool: + """ + Similar to read_non_whitespace, but return a boolean if more than one + whitespace character was read. + + Args: + stream: The data stream from which was read. + + Returns: + True if more than one whitespace was skipped, otherwise return False. + """ + tok = WHITESPACES[0] + cnt = 0 + while tok in WHITESPACES: + tok = stream.read(1) + cnt += 1 + return cnt > 1 + + +def check_if_whitespace_only(value: bytes) -> bool: + """ + Check if the given value consists of whitespace characters only. + + Args: + value: The bytes to check. + + Returns: + True if the value only has whitespace characters, otherwise return False. + """ + for index in range(len(value)): + current = value[index : index + 1] + if current not in WHITESPACES: + return False + return True + + +def skip_over_comment(stream: StreamType) -> None: + tok = stream.read(1) + stream.seek(-1, 1) + if tok == b"%": + while tok not in (b"\n", b"\r"): + tok = stream.read(1) + + +def read_until_regex(stream: StreamType, regex: Pattern[bytes]) -> bytes: + """ + Read until the regular expression pattern matched (ignore the match). + Treats EOF on the underlying stream as the end of the token to be matched. + + Args: + regex: re.Pattern + + Returns: + The read bytes. + """ + name = b"" + while True: + tok = stream.read(16) + if not tok: + return name + m = regex.search(name + tok) + if m is not None: + stream.seek(m.start() - (len(name) + len(tok)), 1) + name = (name + tok)[: m.start()] + break + name += tok + return name + + +def read_block_backwards(stream: StreamType, to_read: int) -> bytes: + """ + Given a stream at position X, read a block of size to_read ending at position X. + + This changes the stream's position to the beginning of where the block was + read. + + Args: + stream: + to_read: + + Returns: + The data which was read. + """ + if stream.tell() < to_read: + raise PdfStreamError("Could not read malformed PDF file") + # Seek to the start of the block we want to read. + stream.seek(-to_read, SEEK_CUR) + read = stream.read(to_read) + # Seek to the start of the block we read after reading it. + stream.seek(-to_read, SEEK_CUR) + return read + + +def read_previous_line(stream: StreamType) -> bytes: + """ + Given a byte stream with current position X, return the previous line. + + All characters between the first CR/LF byte found before X + (or, the start of the file, if no such byte is found) and position X + After this call, the stream will be positioned one byte after the + first non-CRLF character found beyond the first CR/LF byte before X, + or, if no such byte is found, at the beginning of the stream. + + Args: + stream: StreamType: + + Returns: + The data which was read. + """ + line_content = [] + found_crlf = False + if stream.tell() == 0: + raise PdfStreamError(STREAM_TRUNCATED_PREMATURELY) + while True: + to_read = min(DEFAULT_BUFFER_SIZE, stream.tell()) + if to_read == 0: + break + # Read the block. After this, our stream will be one + # beyond the initial position. + block = read_block_backwards(stream, to_read) + idx = len(block) - 1 + if not found_crlf: + # We haven't found our first CR/LF yet. + # Read off characters until we hit one. + while idx >= 0 and block[idx] not in b"\r\n": + idx -= 1 + if idx >= 0: + found_crlf = True + if found_crlf: + # We found our first CR/LF already (on this block or + # a previous one). + # Our combined line is the remainder of the block + # plus any previously read blocks. + line_content.append(block[idx + 1 :]) + # Continue to read off any more CRLF characters. + while idx >= 0 and block[idx] in b"\r\n": + idx -= 1 + else: + # Didn't find CR/LF yet - add this block to our + # previously read blocks and continue. + line_content.append(block) + if idx >= 0: + # We found the next non-CRLF character. + # Set the stream position correctly, then break + stream.seek(idx + 1, SEEK_CUR) + break + # Join all the blocks in the line (which are in reverse order) + return b"".join(line_content[::-1]) + + +def matrix_multiply( + a: TransformationMatrixType, b: TransformationMatrixType +) -> TransformationMatrixType: + return tuple( # type: ignore[return-value] + tuple(sum(float(i) * float(j) for i, j in zip(row, col)) for col in zip(*b)) + for row in a + ) + + +def mark_location(stream: StreamType) -> None: + """Create text file showing current location in context.""" + # Mainly for debugging + radius = 5000 + stream.seek(-radius, 1) + with open("pypdf_pdfLocation.txt", "wb") as output_fh: + output_fh.write(stream.read(radius)) + output_fh.write(b"HERE") + output_fh.write(stream.read(radius)) + stream.seek(-radius, 1) + + +@overload +def ord_(b: str) -> int: + ... + + +@overload +def ord_(b: bytes) -> bytes: + ... + + +@overload +def ord_(b: int) -> int: + ... + + +def ord_(b: Union[int, str, bytes]) -> Union[int, bytes]: + if isinstance(b, str): + return ord(b) + return b + + +WHITESPACES = (b" ", b"\n", b"\r", b"\t", b"\x00") +WHITESPACES_AS_BYTES = b"".join(WHITESPACES) +WHITESPACES_AS_REGEXP = b"[" + WHITESPACES_AS_BYTES + b"]" + + +def deprecate(msg: str, stacklevel: int = 3) -> None: + warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) + + +def deprecation(msg: str) -> None: + raise DeprecationError(msg) + + +def deprecate_with_replacement(old_name: str, new_name: str, removed_in: str) -> None: + """Raise an exception that a feature will be removed, but has a replacement.""" + deprecate( + f"{old_name} is deprecated and will be removed in pypdf {removed_in}. Use {new_name} instead.", + 4, + ) + + +def deprecation_with_replacement(old_name: str, new_name: str, removed_in: str) -> None: + """Raise an exception that a feature was already removed, but has a replacement.""" + deprecation( + f"{old_name} is deprecated and was removed in pypdf {removed_in}. Use {new_name} instead." + ) + + +def deprecate_no_replacement(name: str, removed_in: str) -> None: + """Raise an exception that a feature will be removed without replacement.""" + deprecate(f"{name} is deprecated and will be removed in pypdf {removed_in}.", 4) + + +def deprecation_no_replacement(name: str, removed_in: str) -> None: + """Raise an exception that a feature was already removed without replacement.""" + deprecation(f"{name} is deprecated and was removed in pypdf {removed_in}.") + + +def logger_error(msg: str, src: str) -> None: + """ + Use this instead of logger.error directly. + + That allows people to overwrite it more easily. + + See the docs on when to use which: + https://pypdf.readthedocs.io/en/latest/user/suppress-warnings.html + """ + logging.getLogger(src).error(msg) + + +def logger_warning(msg: str, src: str) -> None: + """ + Use this instead of logger.warning directly. + + That allows people to overwrite it more easily. + + ## Exception, warnings.warn, logger_warning + - Exceptions should be used if the user should write code that deals with + an error case, e.g. the PDF being completely broken. + - warnings.warn should be used if the user needs to fix their code, e.g. + DeprecationWarnings + - logger_warning should be used if the user needs to know that an issue was + handled by pypdf, e.g. a non-compliant PDF being read in a way that + pypdf could apply a robustness fix to still read it. This applies mainly + to strict=False mode. + """ + logging.getLogger(src).warning(msg) + + +def rename_kwargs( + func_name: str, kwargs: Dict[str, Any], aliases: Dict[str, str], fail: bool = False +) -> None: + """ + Helper function to deprecate arguments. + + Args: + func_name: Name of the function to be deprecated + kwargs: + aliases: + fail: + """ + for old_term, new_term in aliases.items(): + if old_term in kwargs: + if fail: + raise DeprecationError( + f"{old_term} is deprecated as an argument. Use {new_term} instead" + ) + if new_term in kwargs: + raise TypeError( + f"{func_name} received both {old_term} and {new_term} as " + f"an argument. {old_term} is deprecated. " + f"Use {new_term} instead." + ) + kwargs[new_term] = kwargs.pop(old_term) + warnings.warn( + message=( + f"{old_term} is deprecated as an argument. Use {new_term} instead" + ), + category=DeprecationWarning, + ) + + +def _human_readable_bytes(bytes: int) -> str: + if bytes < 10**3: + return f"{bytes} Byte" + elif bytes < 10**6: + return f"{bytes / 10**3:.1f} kB" + elif bytes < 10**9: + return f"{bytes / 10**6:.1f} MB" + else: + return f"{bytes / 10**9:.1f} GB" + + +# The following class has been copied from Django: +# https://github.com/django/django/blob/adae619426b6f50046b3daaa744db52989c9d6db/django/utils/functional.py#L51-L65 +# +# Original license: +# +# --------------------------------------------------------------------------------- +# Copyright (c) Django Software Foundation and individual contributors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of Django nor the names of its contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# --------------------------------------------------------------------------------- +class classproperty: # noqa: N801 + """ + Decorator that converts a method with a single cls argument into a property + that can be accessed directly from the class. + """ + + def __init__(self, method=None): # type: ignore # noqa: ANN001 + self.fget = method + + def __get__(self, instance, cls=None) -> Any: # type: ignore # noqa: ANN001 + return self.fget(cls) + + def getter(self, method): # type: ignore # noqa: ANN001, ANN202 + self.fget = method + return self + + +@dataclass +class File: + from .generic import IndirectObject + + name: str = "" + """ + Filename as identified within the PDF file. + """ + data: bytes = b"" + """ + Data as bytes. + """ + indirect_reference: Optional[IndirectObject] = None + """ + Reference to the object storing the stream. + """ + + def __str__(self) -> str: + return f"{self.__class__.__name__}(name={self.name}, data: {_human_readable_bytes(len(self.data))})" + + def __repr__(self) -> str: + return self.__str__()[:-1] + f", hash: {hash(self.data)})" + + +@functools.total_ordering +class Version: + COMPONENT_PATTERN = re.compile(r"^(\d+)(.*)$") + + def __init__(self, version_str: str) -> None: + self.version_str = version_str + self.components = self._parse_version(version_str) + + def _parse_version(self, version_str: str) -> List[Tuple[int, str]]: + components = version_str.split(".") + parsed_components = [] + for component in components: + match = Version.COMPONENT_PATTERN.match(component) + if not match: + parsed_components.append((0, component)) + continue + integer_prefix = match.group(1) + suffix = match.group(2) + if integer_prefix is None: + integer_prefix = 0 + parsed_components.append((int(integer_prefix), suffix)) + return parsed_components + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Version): + return False + return self.components == other.components + + def __lt__(self, other: Any) -> bool: + if not isinstance(other, Version): + raise ValueError(f"Version cannot be compared against {type(other)}") + min_len = min(len(self.components), len(other.components)) + for i in range(min_len): + self_value, self_suffix = self.components[i] + other_value, other_suffix = other.components[i] + + if self_value < other_value: + return True + elif self_value > other_value: + return False + + if self_suffix < other_suffix: + return True + elif self_suffix > other_suffix: + return False + + return len(self.components) < len(other.components) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_version.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_version.py new file mode 100644 index 00000000..ba7be38e --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_version.py @@ -0,0 +1 @@ +__version__ = "5.0.0" diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_writer.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_writer.py new file mode 100644 index 00000000..4d4cca32 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_writer.py @@ -0,0 +1,3291 @@ +# Copyright (c) 2006, Mathieu Fenniak +# Copyright (c) 2007, Ashish Kulkarni +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import decimal +import enum +import hashlib +import re +import struct +import uuid +from io import BytesIO, FileIO, IOBase +from itertools import compress +from pathlib import Path +from types import TracebackType +from typing import ( + IO, + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Pattern, + Tuple, + Type, + Union, + cast, +) + +from ._cmap import _default_fonts_space_width, build_char_map_from_dict +from ._doc_common import DocumentInformation, PdfDocCommon +from ._encryption import EncryptAlgorithm, Encryption +from ._page import PageObject +from ._page_labels import nums_clear_range, nums_insert, nums_next +from ._reader import PdfReader +from ._utils import ( + StrByteType, + StreamType, + _get_max_pdf_version_header, + deprecate, + deprecation_with_replacement, + logger_warning, +) +from .constants import AnnotationDictionaryAttributes as AA +from .constants import CatalogAttributes as CA +from .constants import ( + CatalogDictionary, + FileSpecificationDictionaryEntries, + GoToActionArguments, + ImageType, + InteractiveFormDictEntries, + PageLabelStyle, + TypFitArguments, + UserAccessPermissions, +) +from .constants import Core as CO +from .constants import FieldDictionaryAttributes as FA +from .constants import PageAttributes as PG +from .constants import PagesAttributes as PA +from .constants import TrailerKeys as TK +from .errors import PyPdfError +from .generic import ( + PAGE_FIT, + ArrayObject, + BooleanObject, + ByteStringObject, + ContentStream, + DecodedStreamObject, + Destination, + DictionaryObject, + Fit, + FloatObject, + IndirectObject, + NameObject, + NullObject, + NumberObject, + PdfObject, + RectangleObject, + StreamObject, + TextStringObject, + TreeObject, + ViewerPreferences, + create_string_object, + hex_to_rgb, + is_null_or_none, +) +from .pagerange import PageRange, PageRangeSpec +from .types import ( + AnnotationSubtype, + BorderArrayType, + LayoutType, + OutlineItemType, + OutlineType, + PagemodeType, +) +from .xmp import XmpInformation + +ALL_DOCUMENT_PERMISSIONS = UserAccessPermissions.all() +DEFAULT_FONT_HEIGHT_IN_MULTILINE = 12 + + +class ObjectDeletionFlag(enum.IntFlag): + NONE = 0 + TEXT = enum.auto() + LINKS = enum.auto() + ATTACHMENTS = enum.auto() + OBJECTS_3D = enum.auto() + ALL_ANNOTATIONS = enum.auto() + XOBJECT_IMAGES = enum.auto() + INLINE_IMAGES = enum.auto() + DRAWING_IMAGES = enum.auto() + IMAGES = XOBJECT_IMAGES | INLINE_IMAGES | DRAWING_IMAGES + + +def _rolling_checksum(stream: BytesIO, blocksize: int = 65536) -> str: + hash = hashlib.md5() + for block in iter(lambda: stream.read(blocksize), b""): + hash.update(block) + return hash.hexdigest() + + +class PdfWriter(PdfDocCommon): + """ + Write a PDF file out, given pages produced by another class or through + cloning a PDF file during initialization. + + Typically data is added from a :class:`PdfReader`. + + Args: + clone_from: identical to fileobj (for compatibility) + + incremental: If true, loads the document and set the PdfWriter in incremental mode. + + When writing incrementally, the original document is written first and new/modified + content is appended. To be used for signed document/forms to keep signature valid. + """ + + def __init__( + self, + fileobj: Union[None, PdfReader, StrByteType, Path] = "", + clone_from: Union[None, PdfReader, StrByteType, Path] = None, + incremental: bool = False, + ) -> None: + self.incremental = incremental + """ + Returns if the PdfWriter object has been started in incremental mode. + """ + + self._objects: List[Optional[PdfObject]] = [] + """ + The indirect objects in the PDF. + For the incremental case, it will be filled with None + in clone_reader_document_root. + """ + + self._original_hash: List[int] = [] + """ + List of hashes after import; used to identify changes. + """ + + self._idnum_hash: Dict[bytes, Tuple[IndirectObject, List[IndirectObject]]] = {} + """ + Maps hash values of indirect objects to the list of IndirectObjects. + This is used for compression. + """ + + self._id_translated: Dict[int, Dict[int, int]] = {} + """List of already translated IDs. + dict[id(pdf)][(idnum, generation)] + """ + + self._ID: Union[ArrayObject, None] = None + self._info_obj: Optional[PdfObject] + + if self.incremental: + if isinstance(fileobj, (str, Path)): + with open(fileobj, "rb") as f: + fileobj = BytesIO(f.read(-1)) + if isinstance(fileobj, BytesIO): + fileobj = PdfReader(fileobj) + else: + raise PyPdfError("Invalid type for incremental mode") + self._reader = fileobj # prev content is in _reader.stream + self._header = fileobj.pdf_header.encode() + self._readonly = True # !!!TODO: to be analysed + else: + self._header = b"%PDF-1.3" + self._info_obj = self._add_object( + DictionaryObject( + {NameObject("/Producer"): create_string_object("pypdf")} + ) + ) + + def _get_clone_from( + fileobj: Union[None, PdfReader, str, Path, IO[Any], BytesIO], + clone_from: Union[None, PdfReader, str, Path, IO[Any], BytesIO], + ) -> Union[None, PdfReader, str, Path, IO[Any], BytesIO]: + if not isinstance(fileobj, (str, Path, IO, BytesIO)) or ( + fileobj != "" and clone_from is None + ): + cloning = True + if not ( + not isinstance(fileobj, (str, Path)) + or ( + Path(str(fileobj)).exists() + and Path(str(fileobj)).stat().st_size > 0 + ) + ): + cloning = False + if isinstance(fileobj, (IO, BytesIO)): + t = fileobj.tell() + fileobj.seek(-1, 2) + if fileobj.tell() == 0: + cloning = False + fileobj.seek(t, 0) + if cloning: + clone_from = fileobj + return clone_from + + clone_from = _get_clone_from(fileobj, clone_from) + # to prevent overwriting + self.temp_fileobj = fileobj + self.fileobj = "" + self.with_as_usage = False + # The root of our page tree node. + pages = DictionaryObject() + pages.update( + { + NameObject(PA.TYPE): NameObject("/Pages"), + NameObject(PA.COUNT): NumberObject(0), + NameObject(PA.KIDS): ArrayObject(), + } + ) + self.flattened_pages = [] + self._encryption: Optional[Encryption] = None + self._encrypt_entry: Optional[DictionaryObject] = None + + if clone_from is not None: + if not isinstance(clone_from, PdfReader): + clone_from = PdfReader(clone_from) + self.clone_document_from_reader(clone_from) + else: + self._pages = self._add_object(pages) + # root object + self._root_object = DictionaryObject() + self._root_object.update( + { + NameObject(PA.TYPE): NameObject(CO.CATALOG), + NameObject(CO.PAGES): self._pages, + } + ) + self._add_object(self._root_object) + if isinstance(self._ID, list): + if isinstance(self._ID[0], TextStringObject): + self._ID[0] = ByteStringObject(self._ID[0].get_original_bytes()) + if isinstance(self._ID[1], TextStringObject): + self._ID[1] = ByteStringObject(self._ID[1].get_original_bytes()) + + # for commonality + @property + def is_encrypted(self) -> bool: + """ + Read-only boolean property showing whether this PDF file is encrypted. + + Note that this property, if true, will remain true even after the + :meth:`decrypt()` method is called. + """ + return False + + @property + def root_object(self) -> DictionaryObject: + """ + Provide direct access to PDF Structure. + + Note: + Recommended only for read access. + """ + return self._root_object + + @property + def _info(self) -> Optional[DictionaryObject]: + """ + Provide access to "/Info". Standardized with PdfReader. + + Returns: + /Info Dictionary; None if the entry does not exist + """ + return ( + None + if self._info_obj is None + else cast(DictionaryObject, self._info_obj.get_object()) + ) + + @_info.setter + def _info(self, value: Optional[Union[IndirectObject, DictionaryObject]]) -> None: + if value is None: + try: + self._objects[self._info_obj.indirect_reference.idnum - 1] = None # type: ignore + except (KeyError, AttributeError): + pass + self._info_obj = None + else: + if self._info_obj is None: + self._info_obj = self._add_object(DictionaryObject()) + obj = cast(DictionaryObject, self._info_obj.get_object()) + obj.clear() + obj.update(cast(DictionaryObject, value.get_object())) + + @property + def xmp_metadata(self) -> Optional[XmpInformation]: + """XMP (Extensible Metadata Platform) data.""" + return cast(XmpInformation, self.root_object.xmp_metadata) + + @xmp_metadata.setter + def xmp_metadata(self, value: Optional[XmpInformation]) -> None: + """XMP (Extensible Metadata Platform) data.""" + if value is None: + if "/Metadata" in self.root_object: + del self.root_object["/Metadata"] + else: + self.root_object[NameObject("/Metadata")] = value + + return self.root_object.xmp_metadata # type: ignore + + def __enter__(self) -> "PdfWriter": + """Store that writer is initialized by 'with'.""" + t = self.temp_fileobj + self.__init__() # type: ignore + self.with_as_usage = True + self.fileobj = t # type: ignore + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + """Write data to the fileobj.""" + if self.fileobj: + self.write(self.fileobj) + + def _repr_mimebundle_( + self, + include: Union[None, Iterable[str]] = None, + exclude: Union[None, Iterable[str]] = None, + ) -> Dict[str, Any]: + """ + Integration into Jupyter Notebooks. + + This method returns a dictionary that maps a mime-type to its + representation. + + See https://ipython.readthedocs.io/en/stable/config/integrating.html + """ + pdf_data = BytesIO() + self.write(pdf_data) + data = { + "application/pdf": pdf_data, + } + + if include is not None: + # Filter representations based on include list + data = {k: v for k, v in data.items() if k in include} + + if exclude is not None: + # Remove representations based on exclude list + data = {k: v for k, v in data.items() if k not in exclude} + + return data + + @property + def pdf_header(self) -> str: + """ + Read/Write property of the PDF header that is written. + + This should be something like ``'%PDF-1.5'``. It is recommended to set + the lowest version that supports all features which are used within the + PDF file. + + Note: `pdf_header` returns a string but accepts bytes or str for writing + """ + return self._header.decode() + + @pdf_header.setter + def pdf_header(self, new_header: Union[str, bytes]) -> None: + if isinstance(new_header, str): + new_header = new_header.encode() + self._header = new_header + + def _add_object(self, obj: PdfObject) -> IndirectObject: + if ( + getattr(obj, "indirect_reference", None) is not None + and obj.indirect_reference.pdf == self # type: ignore + ): + return obj.indirect_reference # type: ignore + # check for /Contents in Pages (/Contents in annotation are strings) + if isinstance(obj, DictionaryObject) and isinstance( + obj.get(PG.CONTENTS, None), (ArrayObject, DictionaryObject) + ): + obj[NameObject(PG.CONTENTS)] = self._add_object(obj[PG.CONTENTS]) + self._objects.append(obj) + obj.indirect_reference = IndirectObject(len(self._objects), 0, self) + return obj.indirect_reference + + def get_object( + self, + indirect_reference: Union[int, IndirectObject], + ) -> PdfObject: + if isinstance(indirect_reference, int): + obj = self._objects[indirect_reference - 1] + elif indirect_reference.pdf != self: + raise ValueError("pdf must be self") + else: + obj = self._objects[indirect_reference.idnum - 1] + assert obj is not None # clarification for mypy + return obj + + def _replace_object( + self, + indirect_reference: Union[int, IndirectObject], + obj: PdfObject, + ) -> PdfObject: + if isinstance(indirect_reference, IndirectObject): + if indirect_reference.pdf != self: + raise ValueError("pdf must be self") + indirect_reference = indirect_reference.idnum + gen = self._objects[indirect_reference - 1].indirect_reference.generation # type: ignore + if ( + getattr(obj, "indirect_reference", None) is not None + and obj.indirect_reference.pdf != self # type: ignore + ): + obj = obj.clone(self) + self._objects[indirect_reference - 1] = obj + obj.indirect_reference = IndirectObject(indirect_reference, gen, self) + + assert isinstance(obj, PdfObject) # clarification for mypy + return obj + + def _add_page( + self, + page: PageObject, + index: int, + excluded_keys: Iterable[str] = (), + ) -> PageObject: + if not isinstance(page, PageObject) or page.get(PA.TYPE, None) != CO.PAGE: + raise ValueError("Invalid page object") + assert self.flattened_pages is not None, "for mypy" + page_org = page + excluded_keys = list(excluded_keys) + excluded_keys += [PA.PARENT, "/StructParents"] + # acrobat does not accept to have two indirect ref pointing on the same + # page; therefore in order to add easily multiple copies of the same + # page, we need to create a new dictionary for the page, however the + # objects below (including content) are not duplicated: + try: # delete an already existing page + del self._id_translated[id(page_org.indirect_reference.pdf)][ # type: ignore + page_org.indirect_reference.idnum # type: ignore + ] + except Exception: + pass + page = cast( + "PageObject", page_org.clone(self, False, excluded_keys).get_object() + ) + if page_org.pdf is not None: + other = page_org.pdf.pdf_header + self.pdf_header = _get_max_pdf_version_header(self.pdf_header, other) + node, idx = self._get_page_in_node(index) + page[NameObject(PA.PARENT)] = node.indirect_reference + + if idx >= 0: + cast(ArrayObject, node[PA.KIDS]).insert(idx, page.indirect_reference) + self.flattened_pages.insert(index, page) + else: + cast(ArrayObject, node[PA.KIDS]).append(page.indirect_reference) + self.flattened_pages.append(page) + cpt = 1000 + while not is_null_or_none(node): + node = cast(DictionaryObject, node.get_object()) + node[NameObject(PA.COUNT)] = NumberObject(cast(int, node[PA.COUNT]) + 1) + node = node.get(PA.PARENT, None) + cpt -= 1 + if cpt < 0: + raise PyPdfError("Too many recursive calls!") + return page + + def set_need_appearances_writer(self, state: bool = True) -> None: + """ + Sets the "NeedAppearances" flag in the PDF writer. + + The "NeedAppearances" flag indicates whether the appearance dictionary + for form fields should be automatically generated by the PDF viewer or + if the embedded appearance should be used. + + Args: + state: The actual value of the NeedAppearances flag. + + Returns: + None + """ + # See 12.7.2 and 7.7.2 for more information: + # https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf + try: + # get the AcroForm tree + if CatalogDictionary.ACRO_FORM not in self._root_object: + self._root_object[ + NameObject(CatalogDictionary.ACRO_FORM) + ] = self._add_object(DictionaryObject()) + + need_appearances = NameObject(InteractiveFormDictEntries.NeedAppearances) + cast(DictionaryObject, self._root_object[CatalogDictionary.ACRO_FORM])[ + need_appearances + ] = BooleanObject(state) + except Exception as exc: # pragma: no cover + logger_warning( + f"set_need_appearances_writer({state}) catch : {exc}", __name__ + ) + + def create_viewer_preferences(self) -> ViewerPreferences: + o = ViewerPreferences() + self._root_object[ + NameObject(CatalogDictionary.VIEWER_PREFERENCES) + ] = self._add_object(o) + return o + + def add_page( + self, + page: PageObject, + excluded_keys: Iterable[str] = (), + ) -> PageObject: + """ + Add a page to this PDF file. + + Recommended for advanced usage including the adequate excluded_keys. + + The page is usually acquired from a :class:`PdfReader` + instance. + + Args: + page: The page to add to the document. Should be + an instance of :class:`PageObject` + excluded_keys: + + Returns: + The added PageObject. + """ + assert self.flattened_pages is not None, "mypy" + return self._add_page(page, len(self.flattened_pages), excluded_keys) + + def insert_page( + self, + page: PageObject, + index: int = 0, + excluded_keys: Iterable[str] = (), + ) -> PageObject: + """ + Insert a page in this PDF file. The page is usually acquired from a + :class:`PdfReader` instance. + + Args: + page: The page to add to the document. + index: Position at which the page will be inserted. + excluded_keys: + + Returns: + The added PageObject. + """ + assert self.flattened_pages is not None, "mypy" + if index < 0: + index = len(self.flattened_pages) + index + if index < 0: + raise ValueError("Invalid index value") + if index >= len(self.flattened_pages): + return self.add_page(page, excluded_keys) + else: + return self._add_page(page, index, excluded_keys) + + def _get_page_number_by_indirect( + self, indirect_reference: Union[None, int, NullObject, IndirectObject] + ) -> Optional[int]: + """ + Generate _page_id2num. + + Args: + indirect_reference: + + Returns: + The page number or None + """ + # to provide same function as in PdfReader + if is_null_or_none(indirect_reference): + return None + assert indirect_reference is not None, "mypy" + if isinstance(indirect_reference, int): + indirect_reference = IndirectObject(indirect_reference, 0, self) + obj = indirect_reference.get_object() + if isinstance(obj, PageObject): + return obj.page_number + return None + + def add_blank_page( + self, width: Optional[float] = None, height: Optional[float] = None + ) -> PageObject: + """ + Append a blank page to this PDF file and return it. + + If no page size is specified, use the size of the last page. + + Args: + width: The width of the new page expressed in default user + space units. + height: The height of the new page expressed in default + user space units. + + Returns: + The newly appended page. + + Raises: + PageSizeNotDefinedError: if width and height are not defined + and previous page does not exist. + """ + page = PageObject.create_blank_page(self, width, height) + return self.add_page(page) + + def insert_blank_page( + self, + width: Optional[Union[float, decimal.Decimal]] = None, + height: Optional[Union[float, decimal.Decimal]] = None, + index: int = 0, + ) -> PageObject: + """ + Insert a blank page to this PDF file and return it. + + If no page size is specified, use the size of the last page. + + Args: + width: The width of the new page expressed in default user + space units. + height: The height of the new page expressed in default + user space units. + index: Position to add the page. + + Returns: + The newly inserted page. + + Raises: + PageSizeNotDefinedError: if width and height are not defined + and previous page does not exist. + """ + if width is None or height is None and (self.get_num_pages() - 1) >= index: + oldpage = self.pages[index] + width = oldpage.mediabox.width + height = oldpage.mediabox.height + page = PageObject.create_blank_page(self, width, height) + self.insert_page(page, index) + return page + + @property + def open_destination( + self, + ) -> Union[None, Destination, TextStringObject, ByteStringObject]: + return super().open_destination + + @open_destination.setter + def open_destination(self, dest: Union[None, str, Destination, PageObject]) -> None: + if dest is None: + try: + del self._root_object["/OpenAction"] + except KeyError: + pass + elif isinstance(dest, str): + self._root_object[NameObject("/OpenAction")] = TextStringObject(dest) + elif isinstance(dest, Destination): + self._root_object[NameObject("/OpenAction")] = dest.dest_array + elif isinstance(dest, PageObject): + self._root_object[NameObject("/OpenAction")] = Destination( + "Opening", + dest.indirect_reference + if dest.indirect_reference is not None + else NullObject(), + PAGE_FIT, + ).dest_array + + def add_js(self, javascript: str) -> None: + """ + Add JavaScript which will launch upon opening this PDF. + + Args: + javascript: Your Javascript. + + >>> output.add_js("this.print({bUI:true,bSilent:false,bShrinkToFit:true});") + # Example: This will launch the print window when the PDF is opened. + """ + # Names / JavaScript preferred to be able to add multiple scripts + if "/Names" not in self._root_object: + self._root_object[NameObject(CA.NAMES)] = DictionaryObject() + names = cast(DictionaryObject, self._root_object[CA.NAMES]) + if "/JavaScript" not in names: + names[NameObject("/JavaScript")] = DictionaryObject( + {NameObject("/Names"): ArrayObject()} + ) + js_list = cast( + ArrayObject, cast(DictionaryObject, names["/JavaScript"])["/Names"] + ) + + js = DictionaryObject() + js.update( + { + NameObject(PA.TYPE): NameObject("/Action"), + NameObject("/S"): NameObject("/JavaScript"), + NameObject("/JS"): TextStringObject(f"{javascript}"), + } + ) + # We need a name for parameterized javascript in the pdf file, + # but it can be anything. + js_list.append(create_string_object(str(uuid.uuid4()))) + js_list.append(self._add_object(js)) + + def add_attachment(self, filename: str, data: Union[str, bytes]) -> None: + """ + Embed a file inside the PDF. + + Reference: + https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf + Section 7.11.3 + + Args: + filename: The filename to display. + data: The data in the file. + """ + # We need three entries: + # * The file's data + # * The /Filespec entry + # * The file's name, which goes in the Catalog + + # The entry for the file + # Sample: + # 8 0 obj + # << + # /Length 12 + # /Type /EmbeddedFile + # >> + # stream + # Hello world! + # endstream + # endobj + if isinstance(data, str): + data = data.encode("latin-1") + file_entry = DecodedStreamObject() + file_entry.set_data(data) + file_entry.update({NameObject(PA.TYPE): NameObject("/EmbeddedFile")}) + + # The Filespec entry + # Sample: + # 7 0 obj + # << + # /Type /Filespec + # /F (hello.txt) + # /EF << /F 8 0 R >> + # >> + # endobj + + ef_entry = DictionaryObject() + ef_entry.update({NameObject("/F"): self._add_object(file_entry)}) + + filespec = DictionaryObject() + filespec.update( + { + NameObject(PA.TYPE): NameObject("/Filespec"), + NameObject(FileSpecificationDictionaryEntries.F): create_string_object( + filename + ), # Perhaps also try TextStringObject + NameObject(FileSpecificationDictionaryEntries.EF): ef_entry, + } + ) + + # Then create the entry for the root, as it needs + # a reference to the Filespec + # Sample: + # 1 0 obj + # << + # /Type /Catalog + # /Outlines 2 0 R + # /Pages 3 0 R + # /Names << /EmbeddedFiles << /Names [(hello.txt) 7 0 R] >> >> + # >> + # endobj + + if CA.NAMES not in self._root_object: + self._root_object[NameObject(CA.NAMES)] = self._add_object( + DictionaryObject() + ) + if "/EmbeddedFiles" not in cast(DictionaryObject, self._root_object[CA.NAMES]): + embedded_files_names_dictionary = DictionaryObject( + {NameObject(CA.NAMES): ArrayObject()} + ) + cast(DictionaryObject, self._root_object[CA.NAMES])[ + NameObject("/EmbeddedFiles") + ] = self._add_object(embedded_files_names_dictionary) + else: + embedded_files_names_dictionary = cast( + DictionaryObject, + cast(DictionaryObject, self._root_object[CA.NAMES])["/EmbeddedFiles"], + ) + cast(ArrayObject, embedded_files_names_dictionary[CA.NAMES]).extend( + [create_string_object(filename), filespec] + ) + + def append_pages_from_reader( + self, + reader: PdfReader, + after_page_append: Optional[Callable[[PageObject], None]] = None, + ) -> None: + """ + Copy pages from reader to writer. Includes an optional callback + parameter which is invoked after pages are appended to the writer. + + ``append`` should be preferred. + + Args: + reader: a PdfReader object from which to copy page + annotations to this writer object. The writer's annots + will then be updated. + after_page_append: + Callback function that is invoked after each page is appended to + the writer. Signature includes a reference to the appended page + (delegates to append_pages_from_reader). The single parameter of + the callback is a reference to the page just appended to the + document. + """ + # Get page count from writer and reader + reader_num_pages = len(reader.pages) + # Copy pages from reader to writer + for reader_page_number in range(reader_num_pages): + reader_page = reader.pages[reader_page_number] + writer_page = self.add_page(reader_page) + # Trigger callback, pass writer page as parameter + if callable(after_page_append): + after_page_append(writer_page) + + def _update_field_annotation( + self, + field: DictionaryObject, + anno: DictionaryObject, + font_name: str = "", + font_size: float = -1, + ) -> None: + # Calculate rectangle dimensions + _rct = cast(RectangleObject, anno[AA.Rect]) + rct = RectangleObject((0, 0, abs(_rct[2] - _rct[0]), abs(_rct[3] - _rct[1]))) + + # Extract font information + da = anno.get_inherited( + AA.DA, + cast(DictionaryObject, self.root_object[CatalogDictionary.ACRO_FORM]).get( + AA.DA, None + ), + ) + if da is None: + da = TextStringObject("/Helv 0 Tf 0 g") + else: + da = da.get_object() + font_properties = da.replace("\n", " ").replace("\r", " ").split(" ") + font_properties = [x for x in font_properties if x != ""] + if font_name: + font_properties[font_properties.index("Tf") - 2] = font_name + else: + font_name = font_properties[font_properties.index("Tf") - 2] + font_height = ( + font_size + if font_size >= 0 + else float(font_properties[font_properties.index("Tf") - 1]) + ) + if font_height == 0: + if field.get(FA.Ff, 0) & FA.FfBits.Multiline: + font_height = DEFAULT_FONT_HEIGHT_IN_MULTILINE + else: + font_height = rct.height - 2 + font_properties[font_properties.index("Tf") - 1] = str(font_height) + da = " ".join(font_properties) + y_offset = rct.height - 1 - font_height + + # Retrieve font information from local DR ... + dr: Any = cast( + DictionaryObject, + cast( + DictionaryObject, + anno.get_inherited( + "/DR", + cast( + DictionaryObject, self.root_object[CatalogDictionary.ACRO_FORM] + ).get("/DR", DictionaryObject()), + ), + ).get_object(), + ) + dr = dr.get("/Font", DictionaryObject()).get_object() + # _default_fonts_space_width keys is the list of Standard fonts + if font_name not in dr and font_name not in _default_fonts_space_width: + # ...or AcroForm dictionary + dr = cast( + Dict[Any, Any], + cast( + DictionaryObject, self.root_object[CatalogDictionary.ACRO_FORM] + ).get("/DR", {}), + ) + dr = dr.get_object().get("/Font", DictionaryObject()).get_object() + font_res = dr.get(font_name, None) + if not is_null_or_none(font_res): + font_res = cast(DictionaryObject, font_res.get_object()) + font_subtype, _, font_encoding, font_map = build_char_map_from_dict( + 200, font_res + ) + try: # get rid of width stored in -1 key + del font_map[-1] + except KeyError: + pass + font_full_rev: Dict[str, bytes] + if isinstance(font_encoding, str): + font_full_rev = { + v: k.encode(font_encoding) for k, v in font_map.items() + } + else: + font_full_rev = {v: bytes((k,)) for k, v in font_encoding.items()} + font_encoding_rev = {v: bytes((k,)) for k, v in font_encoding.items()} + for kk, v in font_map.items(): + font_full_rev[v] = font_encoding_rev.get(kk, kk) + else: + logger_warning(f"Font dictionary for {font_name} not found.", __name__) + font_full_rev = {} + + # Retrieve field text and selected values + field_flags = field.get(FA.Ff, 0) + if field.get(FA.FT, "/Tx") == "/Ch" and field_flags & FA.FfBits.Combo == 0: + txt = "\n".join(anno.get_inherited(FA.Opt, [])) + sel = field.get("/V", []) + if not isinstance(sel, list): + sel = [sel] + else: # /Tx + txt = field.get("/V", "") + sel = [] + # Escape parentheses (pdf 1.7 reference, table 3.2 Literal Strings) + txt = txt.replace("\\", "\\\\").replace("(", r"\(").replace(")", r"\)") + # Generate appearance stream + ap_stream = f"q\n/Tx BMC \nq\n1 1 {rct.width - 1} {rct.height - 1} re\nW\nBT\n{da}\n".encode() + for line_number, line in enumerate(txt.replace("\n", "\r").split("\r")): + if line in sel: + # may be improved but cannot find how to get fill working => replaced with lined box + ap_stream += ( + f"1 {y_offset - (line_number * font_height * 1.4) - 1} {rct.width - 2} {font_height + 2} re\n" + f"0.5 0.5 0.5 rg s\n{da}\n" + ).encode() + if line_number == 0: + ap_stream += f"2 {y_offset} Td\n".encode() + else: + # Td is a relative translation + ap_stream += f"0 {- font_height * 1.4} Td\n".encode() + enc_line: List[bytes] = [ + font_full_rev.get(c, c.encode("utf-16-be")) for c in line + ] + if any(len(c) >= 2 for c in enc_line): + ap_stream += b"<" + (b"".join(enc_line)).hex().encode() + b"> Tj\n" + else: + ap_stream += b"(" + b"".join(enc_line) + b") Tj\n" + ap_stream += b"ET\nQ\nEMC\nQ\n" + + # Create appearance dictionary + dct = DecodedStreamObject.initialize_from_dictionary( + { + NameObject("/Type"): NameObject("/XObject"), + NameObject("/Subtype"): NameObject("/Form"), + NameObject("/BBox"): rct, + "__streamdata__": ByteStringObject(ap_stream), + "/Length": 0, + } + ) + if AA.AP in anno: + for k, v in cast(DictionaryObject, anno[AA.AP]).get("/N", {}).items(): + if k not in {"/BBox", "/Length", "/Subtype", "/Type", "/Filter"}: + dct[k] = v + + # Update Resources with font information if necessary + if font_res is not None: + dct[NameObject("/Resources")] = DictionaryObject( + { + NameObject("/Font"): DictionaryObject( + { + NameObject(font_name): getattr( + font_res, "indirect_reference", font_res + ) + } + ) + } + ) + if AA.AP not in anno: + anno[NameObject(AA.AP)] = DictionaryObject( + {NameObject("/N"): self._add_object(dct)} + ) + elif "/N" not in cast(DictionaryObject, anno[AA.AP]): + cast(DictionaryObject, anno[NameObject(AA.AP)])[ + NameObject("/N") + ] = self._add_object(dct) + else: # [/AP][/N] exists + n = anno[AA.AP]["/N"].indirect_reference.idnum # type: ignore + self._objects[n - 1] = dct + dct.indirect_reference = IndirectObject(n, 0, self) + + FFBITS_NUL = FA.FfBits(0) + + def update_page_form_field_values( + self, + page: Union[PageObject, List[PageObject], None], + fields: Dict[str, Any], + flags: FA.FfBits = FFBITS_NUL, + auto_regenerate: Optional[bool] = True, + ) -> None: + """ + Update the form field values for a given page from a fields dictionary. + + Copy field texts and values from fields to page. + If the field links to a parent object, add the information to the parent. + + Args: + page: `PageObject` - references **PDF writer's page** where the + annotations and field data will be updated. + `List[Pageobject]` - provides list of pages to be processed. + `None` - all pages. + fields: a Python dictionary of: + + * field names (/T) as keys and text values (/V) as value + * field names (/T) as keys and list of text values (/V) for multiple choice list + * field names (/T) as keys and tuple of: + * text values (/V) + * font id (e.g. /F1, the font id must exist) + * font size (0 for autosize) + + flags: A set of flags from :class:`~pypdf.constants.FieldDictionaryAttributes.FfBits`. + + auto_regenerate: Set/unset the need_appearances flag; + the flag is unchanged if auto_regenerate is None. + """ + if CatalogDictionary.ACRO_FORM not in self._root_object: + raise PyPdfError("No /AcroForm dictionary in PdfWriter Object") + af = cast(DictionaryObject, self._root_object[CatalogDictionary.ACRO_FORM]) + if InteractiveFormDictEntries.Fields not in af: + raise PyPdfError("No /Fields dictionary in Pdf in PdfWriter Object") + if isinstance(auto_regenerate, bool): + self.set_need_appearances_writer(auto_regenerate) + # Iterate through pages, update field values + if page is None: + page = list(self.pages) + if isinstance(page, list): + for p in page: + if PG.ANNOTS in p: # just to prevent warnings + self.update_page_form_field_values(p, fields, flags, None) + return None + if PG.ANNOTS not in page: + logger_warning("No fields to update on this page", __name__) + return + for writer_annot in page[PG.ANNOTS]: # type: ignore + writer_annot = cast(DictionaryObject, writer_annot.get_object()) + if writer_annot.get("/Subtype", "") != "/Widget": + continue + if "/FT" in writer_annot and "/T" in writer_annot: + writer_parent_annot = writer_annot + else: + writer_parent_annot = writer_annot.get( + PG.PARENT, DictionaryObject() + ).get_object() + + for field, value in fields.items(): + if not ( + self._get_qualified_field_name(writer_parent_annot) == field + or writer_parent_annot.get("/T", None) == field + ): + continue + if ( + writer_parent_annot.get("/FT", None) == "/Ch" + and "/I" in writer_parent_annot + ): + del writer_parent_annot["/I"] + if flags: + writer_annot[NameObject(FA.Ff)] = NumberObject(flags) + if isinstance(value, list): + lst = ArrayObject(TextStringObject(v) for v in value) + writer_parent_annot[NameObject(FA.V)] = lst + elif isinstance(value, tuple): + writer_annot[NameObject(FA.V)] = TextStringObject( + value[0], + ) + else: + writer_parent_annot[NameObject(FA.V)] = TextStringObject(value) + if writer_parent_annot.get(FA.FT) in ("/Btn"): + # case of Checkbox button (no /FT found in Radio widgets + v = NameObject(value) + if v not in writer_annot[NameObject(AA.AP)][NameObject("/N")]: + v = NameObject("/Off") + # other cases will be updated through the for loop + writer_annot[NameObject(AA.AS)] = v + elif ( + writer_parent_annot.get(FA.FT) == "/Tx" + or writer_parent_annot.get(FA.FT) == "/Ch" + ): + # textbox + if isinstance(value, tuple): + self._update_field_annotation( + writer_parent_annot, writer_annot, value[1], value[2] + ) + else: + self._update_field_annotation(writer_parent_annot, writer_annot) + elif ( + writer_annot.get(FA.FT) == "/Sig" + ): # deprecated # not implemented yet + # signature + logger_warning("Signature forms not implemented yet", __name__) + + def reattach_fields( + self, page: Optional[PageObject] = None + ) -> List[DictionaryObject]: + """ + Parse annotations within the page looking for orphan fields and + reattach then into the Fields Structure. + + Args: + page: page to analyze. + If none is provided, all pages will be analyzed. + + Returns: + list of reattached fields. + """ + lst = [] + if page is None: + for p in self.pages: + lst += self.reattach_fields(p) + return lst + + try: + af = cast(DictionaryObject, self._root_object[CatalogDictionary.ACRO_FORM]) + except KeyError: + af = DictionaryObject() + self._root_object[NameObject(CatalogDictionary.ACRO_FORM)] = af + try: + fields = cast(ArrayObject, af[InteractiveFormDictEntries.Fields]) + except KeyError: + fields = ArrayObject() + af[NameObject(InteractiveFormDictEntries.Fields)] = fields + + if "/Annots" not in page: + return lst + annots = cast(ArrayObject, page["/Annots"]) + for idx in range(len(annots)): + ano = annots[idx] + indirect = isinstance(ano, IndirectObject) + ano = cast(DictionaryObject, ano.get_object()) + if ano.get("/Subtype", "") == "/Widget" and "/FT" in ano: + if ( + "indirect_reference" in ano.__dict__ + and ano.indirect_reference in fields + ): + continue + if not indirect: + annots[idx] = self._add_object(ano) + fields.append(ano.indirect_reference) + lst.append(ano) + return lst + + def clone_reader_document_root(self, reader: PdfReader) -> None: + """ + Copy the reader document root to the writer and all sub-elements, + including pages, threads, outlines,... For partial insertion, ``append`` + should be considered. + + Args: + reader: PdfReader from which the document root should be copied. + """ + if self.incremental: + self._objects = [None] * cast(int, reader.trailer["/Size"]) + else: + self._objects.clear() + self._info_obj = None + self._root_object = reader.root_object.clone(self) + self._pages = self._root_object.raw_get("/Pages") + + assert len(self._objects) <= cast(int, reader.trailer["/Size"]) # for pytest + # must be done here before rewriting + if self.incremental: + self._original_hash = [ + (obj.hash_bin() if obj is not None else 0) for obj in self._objects + ] + self._flatten() + assert self.flattened_pages is not None + for p in self.flattened_pages: + self._replace_object(cast(IndirectObject, p.indirect_reference).idnum, p) + if not self.incremental: + p[NameObject("/Parent")] = self._pages + if not self.incremental: + cast(DictionaryObject, self._pages.get_object())[ + NameObject("/Kids") + ] = ArrayObject([p.indirect_reference for p in self.flattened_pages]) + + def clone_document_from_reader( + self, + reader: PdfReader, + after_page_append: Optional[Callable[[PageObject], None]] = None, + ) -> None: + """ + Create a copy (clone) of a document from a PDF file reader cloning + section '/Root' and '/Info' and '/ID' of the pdf. + + Args: + reader: PDF file reader instance from which the clone + should be created. + after_page_append: + Callback function that is invoked after each page is appended to + the writer. Signature includes a reference to the appended page + (delegates to append_pages_from_reader). The single parameter of + the callback is a reference to the page just appended to the + document. + """ + self.clone_reader_document_root(reader) + inf = reader._info + if self.incremental: + if inf is not None: + self._info_obj = cast( + IndirectObject, inf.clone(self).indirect_reference + ) + assert isinstance(self._info, DictionaryObject), "for mypy" + self._original_hash[ + self._info_obj.indirect_reference.idnum - 1 + ] = self._info.hash_bin() + elif inf is not None: + self._info_obj = self._add_object( + DictionaryObject(cast(DictionaryObject, inf.get_object())) + ) + # else: _info_obj = None done in clone_reader_document_root() + + try: + self._ID = cast(ArrayObject, reader._ID).clone(self) + except AttributeError: + pass + + if callable(after_page_append): + for page in cast( + ArrayObject, cast(DictionaryObject, self._pages.get_object())["/Kids"] + ): + after_page_append(page.get_object()) + + def _compute_document_identifier(self) -> ByteStringObject: + stream = BytesIO() + self._write_pdf_structure(stream) + stream.seek(0) + return ByteStringObject(_rolling_checksum(stream).encode("utf8")) + + def generate_file_identifiers(self) -> None: + """ + Generate an identifier for the PDF that will be written. + + The only point of this is ensuring uniqueness. Reproducibility is not + required. + When a file is first written, both identifiers shall be set to the same value. + If both identifiers match when a file reference is resolved, it is very + likely that the correct and unchanged file has been found. If only the first + identifier matches, a different version of the correct file has been found. + see 14.4 "File Identifiers". + """ + if self._ID: + id1 = self._ID[0] + id2 = self._compute_document_identifier() + else: + id1 = self._compute_document_identifier() + id2 = id1 + self._ID = ArrayObject((id1, id2)) + + def encrypt( + self, + user_password: str, + owner_password: Optional[str] = None, + use_128bit: bool = True, + permissions_flag: UserAccessPermissions = ALL_DOCUMENT_PERMISSIONS, + *, + algorithm: Optional[str] = None, + ) -> None: + """ + Encrypt this PDF file with the PDF Standard encryption handler. + + Args: + user_password: The password which allows for opening + and reading the PDF file with the restrictions provided. + owner_password: The password which allows for + opening the PDF files without any restrictions. By default, + the owner password is the same as the user password. + use_128bit: flag as to whether to use 128bit + encryption. When false, 40bit encryption will be used. + By default, this flag is on. + permissions_flag: permissions as described in + Table 3.20 of the PDF 1.7 specification. A bit value of 1 means + the permission is granted. + Hence an integer value of -1 will set all flags. + Bit position 3 is for printing, 4 is for modifying content, + 5 and 6 control annotations, 9 for form fields, + 10 for extraction of text and graphics. + algorithm: encrypt algorithm. Values may be one of "RC4-40", "RC4-128", + "AES-128", "AES-256-R5", "AES-256". If it is valid, + `use_128bit` will be ignored. + """ + if owner_password is None: + owner_password = user_password + + if algorithm is not None: + try: + alg = getattr(EncryptAlgorithm, algorithm.replace("-", "_")) + except AttributeError: + raise ValueError(f"algorithm '{algorithm}' NOT supported") + else: + alg = EncryptAlgorithm.RC4_128 + if not use_128bit: + alg = EncryptAlgorithm.RC4_40 + self.generate_file_identifiers() + assert self._ID + self._encryption = Encryption.make(alg, permissions_flag, self._ID[0]) + # in case call `encrypt` again + entry = self._encryption.write_entry(user_password, owner_password) + if self._encrypt_entry: + # replace old encrypt_entry + assert self._encrypt_entry.indirect_reference is not None + entry.indirect_reference = self._encrypt_entry.indirect_reference + self._objects[entry.indirect_reference.idnum - 1] = entry + else: + self._add_object(entry) + self._encrypt_entry = entry + + def write_stream(self, stream: StreamType) -> None: + if hasattr(stream, "mode") and "b" not in stream.mode: + logger_warning( + f"File <{stream.name}> to write to is not in binary mode. " + "It may not be written to correctly.", + __name__, + ) + # deprecated to be removed in pypdf 6.0.0 : + # if not self._root: + # self._root = self._add_object(self._root_object) + # self._sweep_indirect_references(self._root) + + if self.incremental: + self._reader.stream.seek(0) + stream.write(self._reader.stream.read(-1)) + if len(self.list_objects_in_increment()) > 0: + self._write_increment(stream) # writes objs, Xref stream and startx + else: + object_positions, free_objects = self._write_pdf_structure(stream) + xref_location = self._write_xref_table( + stream, object_positions, free_objects + ) + self._write_trailer(stream, xref_location) + + def write(self, stream: Union[Path, StrByteType]) -> Tuple[bool, IO[Any]]: + """ + Write the collection of pages added to this object out as a PDF file. + + Args: + stream: An object to write the file to. The object can support + the write method and the tell method, similar to a file object, or + be a file path, just like the fileobj, just named it stream to keep + existing workflow. + + Returns: + A tuple (bool, IO). + """ + my_file = False + + if stream == "": + raise ValueError(f"Output(stream={stream}) is empty.") + + if isinstance(stream, (str, Path)): + stream = FileIO(stream, "wb") + self.with_as_usage = True # + my_file = True + + self.write_stream(stream) + + if self.with_as_usage: + stream.close() + + return my_file, stream + + def list_objects_in_increment(self) -> List[IndirectObject]: + """ + For debugging/analysis. + Provides the list of new/modified objects that will be written + in the increment. + Deleted objects will not be freed but will become orphans. + + Returns: + List of (new / modified) IndirectObjects + """ + return [ + cast(IndirectObject, self._objects[i]).indirect_reference + for i in range(len(self._objects)) + if ( + self._objects[i] is not None + and ( + i >= len(self._original_hash) + or cast(PdfObject, self._objects[i]).hash_bin() + != self._original_hash[i] + ) + ) + ] + + def _write_increment(self, stream: StreamType) -> None: + object_positions = {} + object_blocks = [] + current_start = -1 + current_stop = -2 + for i, obj in enumerate(self._objects): + if self._objects[i] is not None and ( + i >= len(self._original_hash) + or cast(PdfObject, self._objects[i]).hash_bin() + != self._original_hash[i] + ): + idnum = i + 1 + assert isinstance(obj, PdfObject) # mypy + # first write new/modified object + object_positions[idnum] = stream.tell() + stream.write(f"{idnum} 0 obj\n".encode()) + """ encryption is not operational + if self._encryption and obj != self._encrypt_entry: + obj = self._encryption.encrypt_object(obj, idnum, 0) + """ + obj.write_to_stream(stream) + stream.write(b"\nendobj\n") + + # prepare xref + if idnum != current_stop: + if current_start > 0: + object_blocks.append( + [current_start, current_stop - current_start] + ) + current_start = idnum + current_stop = idnum + 1 + assert current_start > 0, "for pytest only" + object_blocks.append([current_start, current_stop - current_start]) + # write incremented xref + xref_location = stream.tell() + xr_id = len(self._objects) + 1 + stream.write(f"{xr_id} 0 obj".encode()) + init_data = { + NameObject("/Type"): NameObject("/XRef"), + NameObject("/Size"): NumberObject(xr_id + 1), + NameObject("/Root"): self.root_object.indirect_reference, + NameObject("/Filter"): NameObject("/FlateDecode"), + NameObject("/Index"): ArrayObject( + [NumberObject(_it) for _su in object_blocks for _it in _su] + ), + NameObject("/W"): ArrayObject( + [NumberObject(1), NumberObject(4), NumberObject(1)] + ), + "__streamdata__": b"", + } + if self._info is not None and ( + self._info.indirect_reference.idnum - 1 # type: ignore + >= len(self._original_hash) + or cast(IndirectObject, self._info).hash_bin() # kept for future + != self._original_hash[ + self._info.indirect_reference.idnum - 1 # type: ignore + ] + ): + init_data[NameObject(TK.INFO)] = self._info.indirect_reference + init_data[NameObject(TK.PREV)] = NumberObject(self._reader._startxref) + if self._ID: + init_data[NameObject(TK.ID)] = self._ID + xr = StreamObject.initialize_from_dictionary(init_data) + xr.set_data( + b"".join( + [struct.pack(b">BIB", 1, _pos, 0) for _pos in object_positions.values()] + ) + ) + xr.write_to_stream(stream) + stream.write(f"\nstartxref\n{xref_location}\n%%EOF\n".encode()) # eof + + def _write_pdf_structure(self, stream: StreamType) -> Tuple[List[int], List[int]]: + object_positions = [] + free_objects = [] # will contain list of all free entries + stream.write(self.pdf_header.encode() + b"\n") + stream.write(b"%\xE2\xE3\xCF\xD3\n") + + for i, obj in enumerate(self._objects): + if obj is not None: + idnum = i + 1 + object_positions.append(stream.tell()) + stream.write(f"{idnum} 0 obj\n".encode()) + if self._encryption and obj != self._encrypt_entry: + obj = self._encryption.encrypt_object(obj, idnum, 0) + obj.write_to_stream(stream) + stream.write(b"\nendobj\n") + else: + object_positions.append(-1) + free_objects.append(i + 1) + free_objects.append(0) # add 0 to loop in accordance with PDF spec + return object_positions, free_objects + + def _write_xref_table( + self, stream: StreamType, object_positions: List[int], free_objects: List[int] + ) -> int: + xref_location = stream.tell() + stream.write(b"xref\n") + stream.write(f"0 {len(self._objects) + 1}\n".encode()) + stream.write(f"{free_objects[0]:0>10} {65535:0>5} f \n".encode()) + free_idx = 1 + for offset in object_positions: + if offset > 0: + stream.write(f"{offset:0>10} {0:0>5} n \n".encode()) + else: + stream.write(f"{free_objects[free_idx]:0>10} {1:0>5} f \n".encode()) + free_idx += 1 + return xref_location + + def _write_trailer(self, stream: StreamType, xref_location: int) -> None: + """ + Write the PDF trailer to the stream. + + To quote the PDF specification: + [The] trailer [gives] the location of the cross-reference table and + of certain special objects within the body of the file. + """ + stream.write(b"trailer\n") + trailer = DictionaryObject( + { + NameObject(TK.SIZE): NumberObject(len(self._objects) + 1), + NameObject(TK.ROOT): self.root_object.indirect_reference, + } + ) + if self._info is not None: + trailer[NameObject(TK.INFO)] = self._info.indirect_reference + if self._ID is not None: + trailer[NameObject(TK.ID)] = self._ID + if self._encrypt_entry: + trailer[NameObject(TK.ENCRYPT)] = self._encrypt_entry.indirect_reference + trailer.write_to_stream(stream) + stream.write(f"\nstartxref\n{xref_location}\n%%EOF\n".encode()) # eof + + @property + def metadata(self) -> Optional[DocumentInformation]: + """ + Retrieve/set the PDF file's document information dictionary, if it exists. + + Args: + value: dict with the entries to be set. if None : remove the /Info entry from the pdf. + + Note that some PDF files use (xmp)metadata streams instead of document + information dictionaries, and these metadata streams will not be + accessed by this function. + """ + return super().metadata + + @metadata.setter + def metadata( + self, + value: Optional[Union[DocumentInformation, DictionaryObject, Dict[Any, Any]]], + ) -> None: + if value is None: + self._info = None + else: + if self._info is not None: + self._info.clear() + else: + self._info = DictionaryObject() + self.add_metadata(value) + + def add_metadata(self, infos: Dict[str, Any]) -> None: + """ + Add custom metadata to the output. + + Args: + infos: a Python dictionary where each key is a field + and each value is your new metadata. + """ + args = {} + if isinstance(infos, PdfObject): + infos = cast(DictionaryObject, infos.get_object()) + for key, value in list(infos.items()): + if isinstance(value, PdfObject): + value = value.get_object() + args[NameObject(key)] = create_string_object(str(value)) + assert isinstance(self._info, DictionaryObject) + self._info.update(args) + + def compress_identical_objects( + self, + remove_identicals: bool = True, + remove_orphans: bool = True, + ) -> None: + """ + Parse the PDF file and merge objects that have same hash. + This will make objects common to multiple pages. + Recommended to be used just before writing output. + + Args: + remove_identicals: Remove identical objects. + remove_orphans: Remove unreferenced objects. + """ + + def replace_in_obj( + obj: PdfObject, crossref: Dict[IndirectObject, IndirectObject] + ) -> None: + if isinstance(obj, DictionaryObject): + key_val = obj.items() + elif isinstance(obj, ArrayObject): + key_val = enumerate(obj) # type: ignore + else: + return + assert isinstance(obj, (DictionaryObject, ArrayObject)) + for k, v in key_val: + if isinstance(v, IndirectObject): + orphans[v.idnum - 1] = False + if v in crossref: + obj[k] = crossref[v] + else: + """the filtering on DictionaryObject and ArrayObject only + will be performed within replace_in_obj""" + replace_in_obj(v, crossref) + + # _idnum_hash :dict[hash]=(1st_ind_obj,[other_indir_objs,...]) + self._idnum_hash = {} + orphans = [True] * len(self._objects) + # look for similar objects + for idx, obj in enumerate(self._objects): + if obj is None: + continue + assert isinstance(obj.indirect_reference, IndirectObject) + h = obj.hash_value() + if remove_identicals and h in self._idnum_hash: + self._idnum_hash[h][1].append(obj.indirect_reference) + self._objects[idx] = None + else: + self._idnum_hash[h] = (obj.indirect_reference, []) + + # generate the dict converting others to 1st + cnv = {v[0]: v[1] for v in self._idnum_hash.values() if len(v[1]) > 0} + cnv_rev: Dict[IndirectObject, IndirectObject] = {} + for k, v in cnv.items(): + cnv_rev.update(zip(v, (k,) * len(v))) + + # replace reference to merged objects + for obj in self._objects: + if isinstance(obj, (DictionaryObject, ArrayObject)): + replace_in_obj(obj, cnv_rev) + + # remove orphans (if applicable) + orphans[self.root_object.indirect_reference.idnum - 1] = False # type: ignore + + orphans[self._info.indirect_reference.idnum - 1] = False # type: ignore + + try: + orphans[self._ID.indirect_reference.idnum - 1] = False # type: ignore + except AttributeError: + pass + for i in compress(range(len(self._objects)), orphans): + self._objects[i] = None + + def _sweep_indirect_references( + self, + root: Union[ + ArrayObject, + BooleanObject, + DictionaryObject, + FloatObject, + IndirectObject, + NameObject, + PdfObject, + NumberObject, + TextStringObject, + NullObject, + ], + ) -> None: # deprecated + """ + Resolving any circular references to Page objects. + + Circular references to Page objects can arise when objects such as + annotations refer to their associated page. If these references are not + properly handled, the PDF file will contain multiple copies of the same + Page object. To address this problem, Page objects store their original + object reference number. This method adds the reference number of any + circularly referenced Page objects to an external reference map. This + ensures that self-referencing trees reference the correct new object + location, rather than copying in a new copy of the Page object. + + Args: + root: The root of the PDF object tree to sweep. + """ + deprecate( + "_sweep_indirect_references has been removed, please report to dev team if this warning is observed", + ) + + def _resolve_indirect_object( + self, data: IndirectObject + ) -> IndirectObject: # deprecated + """ + Resolves an indirect object to an indirect object in this PDF file. + + If the input indirect object already belongs to this PDF file, it is + returned directly. Otherwise, the object is retrieved from the input + object's PDF file using the object's ID number and generation number. If + the object cannot be found, a warning is logged and a `NullObject` is + returned. + + If the object is not already in this PDF file, it is added to the file's + list of objects and assigned a new ID number and generation number of 0. + The hash value of the object is then added to the `_idnum_hash` + dictionary, with the corresponding `IndirectObject` reference as the + value. + + Args: + data: The `IndirectObject` to resolve. + + Returns: + The resolved `IndirectObject` in this PDF file. + + Raises: + ValueError: If the input stream is closed. + """ + deprecate( + "_resolve_indirect_object has been removed, please report to dev team if this warning is observed", + ) + return IndirectObject(0, 0, self) + + def get_reference(self, obj: PdfObject) -> IndirectObject: + idnum = self._objects.index(obj) + 1 + ref = IndirectObject(idnum, 0, self) + assert ref.get_object() == obj + return ref + + def get_outline_root(self) -> TreeObject: + if CO.OUTLINES in self._root_object: + # Table 3.25 Entries in the catalog dictionary + outline = cast(TreeObject, self._root_object[CO.OUTLINES]) + if not isinstance(outline, TreeObject): + t = TreeObject(outline) + self._replace_object(outline.indirect_reference.idnum, t) + outline = t + idnum = self._objects.index(outline) + 1 + outline_ref = IndirectObject(idnum, 0, self) + assert outline_ref.get_object() == outline + else: + outline = TreeObject() + outline.update({}) + outline_ref = self._add_object(outline) + self._root_object[NameObject(CO.OUTLINES)] = outline_ref + + return outline + + def get_threads_root(self) -> ArrayObject: + """ + The list of threads. + + See §12.4.3 of the PDF 1.7 or PDF 2.0 specification. + + Returns: + An array (possibly empty) of Dictionaries with ``/F`` and + ``/I`` properties. + """ + if CO.THREADS in self._root_object: + # Table 3.25 Entries in the catalog dictionary + threads = cast(ArrayObject, self._root_object[CO.THREADS]) + else: + threads = ArrayObject() + self._root_object[NameObject(CO.THREADS)] = threads + return threads + + @property + def threads(self) -> ArrayObject: + """ + Read-only property for the list of threads. + + See §8.3.2 from PDF 1.7 spec. + + Each element is a dictionaries with ``/F`` and ``/I`` keys. + """ + return self.get_threads_root() + + def add_outline_item_destination( + self, + page_destination: Union[IndirectObject, PageObject, TreeObject], + parent: Union[None, TreeObject, IndirectObject] = None, + before: Union[None, TreeObject, IndirectObject] = None, + is_open: bool = True, + ) -> IndirectObject: + page_destination = cast(PageObject, page_destination.get_object()) + if isinstance(page_destination, PageObject): + return self.add_outline_item_destination( + Destination( + f"page #{page_destination.page_number}", + cast(IndirectObject, page_destination.indirect_reference), + Fit.fit(), + ) + ) + + if parent is None: + parent = self.get_outline_root() + + page_destination[NameObject("/%is_open%")] = BooleanObject(is_open) + parent = cast(TreeObject, parent.get_object()) + page_destination_ref = self._add_object(page_destination) + if before is not None: + before = before.indirect_reference + parent.insert_child( + page_destination_ref, + before, + self, + page_destination.inc_parent_counter_outline + if is_open + else (lambda x, y: 0), + ) + if "/Count" not in page_destination: + page_destination[NameObject("/Count")] = NumberObject(0) + + return page_destination_ref + + def add_outline_item_dict( + self, + outline_item: OutlineItemType, + parent: Union[None, TreeObject, IndirectObject] = None, + before: Union[None, TreeObject, IndirectObject] = None, + is_open: bool = True, + ) -> IndirectObject: + outline_item_object = TreeObject() + outline_item_object.update(outline_item) + + """code currently unreachable + if "/A" in outline_item: + action = DictionaryObject() + a_dict = cast(DictionaryObject, outline_item["/A"]) + for k, v in list(a_dict.items()): + action[NameObject(str(k))] = v + action_ref = self._add_object(action) + outline_item_object[NameObject("/A")] = action_ref + """ + return self.add_outline_item_destination( + outline_item_object, parent, before, is_open + ) + + def add_outline_item( + self, + title: str, + page_number: Union[None, PageObject, IndirectObject, int], + parent: Union[None, TreeObject, IndirectObject] = None, + before: Union[None, TreeObject, IndirectObject] = None, + color: Optional[Union[Tuple[float, float, float], str]] = None, + bold: bool = False, + italic: bool = False, + fit: Fit = PAGE_FIT, + is_open: bool = True, + ) -> IndirectObject: + """ + Add an outline item (commonly referred to as a "Bookmark") to the PDF file. + + Args: + title: Title to use for this outline item. + page_number: Page number this outline item will point to. + parent: A reference to a parent outline item to create nested + outline items. + before: + color: Color of the outline item's font as a red, green, blue tuple + from 0.0 to 1.0 or as a Hex String (#RRGGBB) + bold: Outline item font is bold + italic: Outline item font is italic + fit: The fit of the destination page. + + Returns: + The added outline item as an indirect object. + """ + page_ref: Union[None, NullObject, IndirectObject, NumberObject] + if isinstance(italic, Fit): # it means that we are on the old params + if fit is not None and page_number is None: + page_number = fit # type: ignore + return self.add_outline_item( + title, page_number, parent, None, before, color, bold, italic, is_open=is_open # type: ignore + ) + if page_number is None: + action_ref = None + else: + if isinstance(page_number, IndirectObject): + page_ref = page_number + elif isinstance(page_number, PageObject): + page_ref = page_number.indirect_reference + elif isinstance(page_number, int): + try: + page_ref = self.pages[page_number].indirect_reference + except IndexError: + page_ref = NumberObject(page_number) + if page_ref is None: + logger_warning( + f"can not find reference of page {page_number}", + __name__, + ) + page_ref = NullObject() + dest = Destination( + NameObject("/" + title + " outline item"), + page_ref, + fit, + ) + + action_ref = self._add_object( + DictionaryObject( + { + NameObject(GoToActionArguments.D): dest.dest_array, + NameObject(GoToActionArguments.S): NameObject("/GoTo"), + } + ) + ) + outline_item = self._add_object( + _create_outline_item(action_ref, title, color, italic, bold) + ) + + if parent is None: + parent = self.get_outline_root() + return self.add_outline_item_destination(outline_item, parent, before, is_open) + + def add_outline(self) -> None: + raise NotImplementedError( + "This method is not yet implemented. Use :meth:`add_outline_item` instead." + ) + + def add_named_destination_array( + self, title: TextStringObject, destination: Union[IndirectObject, ArrayObject] + ) -> None: + named_dest = self.get_named_dest_root() + i = 0 + while i < len(named_dest): + if title < named_dest[i]: + named_dest.insert(i, destination) + named_dest.insert(i, TextStringObject(title)) + return + else: + i += 2 + named_dest.extend([TextStringObject(title), destination]) + return + + def add_named_destination_object( + self, + page_destination: PdfObject, + ) -> IndirectObject: + page_destination_ref = self._add_object(page_destination.dest_array) # type: ignore + self.add_named_destination_array( + cast("TextStringObject", page_destination["/Title"]), page_destination_ref # type: ignore + ) + + return page_destination_ref + + def add_named_destination( + self, + title: str, + page_number: int, + ) -> IndirectObject: + page_ref = self.get_object(self._pages)[PA.KIDS][page_number] # type: ignore + dest = DictionaryObject() + dest.update( + { + NameObject(GoToActionArguments.D): ArrayObject( + [page_ref, NameObject(TypFitArguments.FIT_H), NumberObject(826)] + ), + NameObject(GoToActionArguments.S): NameObject("/GoTo"), + } + ) + + dest_ref = self._add_object(dest) + if not isinstance(title, TextStringObject): + title = TextStringObject(str(title)) + + self.add_named_destination_array(title, dest_ref) + return dest_ref + + def remove_links(self) -> None: + """Remove links and annotations from this output.""" + for page in self.pages: + self.remove_objects_from_page(page, ObjectDeletionFlag.ALL_ANNOTATIONS) + + def remove_annotations( + self, subtypes: Optional[Union[AnnotationSubtype, Iterable[AnnotationSubtype]]] + ) -> None: + """ + Remove annotations by annotation subtype. + + Args: + subtypes: subtype or list of subtypes to be removed. + Examples are: "/Link", "/FileAttachment", "/Sound", + "/Movie", "/Screen", ... + If you want to remove all annotations, use subtypes=None. + """ + for page in self.pages: + self._remove_annots_from_page(page, subtypes) + + def _remove_annots_from_page( + self, + page: Union[IndirectObject, PageObject, DictionaryObject], + subtypes: Optional[Iterable[str]], + ) -> None: + page = cast(DictionaryObject, page.get_object()) + if PG.ANNOTS in page: + i = 0 + while i < len(cast(ArrayObject, page[PG.ANNOTS])): + an = cast(ArrayObject, page[PG.ANNOTS])[i] + obj = cast(DictionaryObject, an.get_object()) + if subtypes is None or cast(str, obj["/Subtype"]) in subtypes: + if isinstance(an, IndirectObject): + self._objects[an.idnum - 1] = NullObject() # to reduce PDF size + del page[PG.ANNOTS][i] # type:ignore + else: + i += 1 + + def remove_objects_from_page( + self, + page: Union[PageObject, DictionaryObject], + to_delete: Union[ObjectDeletionFlag, Iterable[ObjectDeletionFlag]], + ) -> None: + """ + Remove objects specified by ``to_delete`` from the given page. + + Args: + page: Page object to clean up. + to_delete: Objects to be deleted; can be a ``ObjectDeletionFlag`` + or a list of ObjectDeletionFlag + """ + if isinstance(to_delete, (list, tuple)): + for to_d in to_delete: + self.remove_objects_from_page(page, to_d) + return + assert isinstance(to_delete, ObjectDeletionFlag) + + if to_delete & ObjectDeletionFlag.LINKS: + return self._remove_annots_from_page(page, ("/Link",)) + if to_delete & ObjectDeletionFlag.ATTACHMENTS: + return self._remove_annots_from_page( + page, ("/FileAttachment", "/Sound", "/Movie", "/Screen") + ) + if to_delete & ObjectDeletionFlag.OBJECTS_3D: + return self._remove_annots_from_page(page, ("/3D",)) + if to_delete & ObjectDeletionFlag.ALL_ANNOTATIONS: + return self._remove_annots_from_page(page, None) + + jump_operators = [] + if to_delete & ObjectDeletionFlag.DRAWING_IMAGES: + jump_operators = ( + [b"w", b"J", b"j", b"M", b"d", b"i"] + + [b"W", b"W*"] + + [b"b", b"b*", b"B", b"B*", b"S", b"s", b"f", b"f*", b"F", b"n"] + + [b"m", b"l", b"c", b"v", b"y", b"h", b"re"] + + [b"sh"] + ) + if to_delete & ObjectDeletionFlag.TEXT: + jump_operators = [b"Tj", b"TJ", b"'", b'"'] + + def clean(content: ContentStream, images: List[str], forms: List[str]) -> None: + nonlocal jump_operators, to_delete + i = 0 + while i < len(content.operations): + operands, operator = content.operations[i] + if ( + ( + operator == b"INLINE IMAGE" + and (to_delete & ObjectDeletionFlag.INLINE_IMAGES) + ) + or (operator in jump_operators) + or ( + operator == b"Do" + and (to_delete & ObjectDeletionFlag.XOBJECT_IMAGES) + and (operands[0] in images) + ) + ): + del content.operations[i] + else: + i += 1 + content.get_data() # this ensures ._data is rebuilt from the .operations + + def clean_forms( + elt: DictionaryObject, stack: List[DictionaryObject] + ) -> Tuple[List[str], List[str]]: + nonlocal to_delete + # elt in recursive call is a new ContentStream object, so we have to check the indirect_reference + if (elt in stack) or ( + hasattr(elt, "indirect_reference") + and any( + elt.indirect_reference == getattr(x, "indirect_reference", -1) + for x in stack + ) + ): + # to prevent infinite looping + return [], [] # pragma: no cover + try: + d = cast( + Dict[Any, Any], + cast(DictionaryObject, elt["/Resources"])["/XObject"], + ) + except KeyError: + d = {} + images = [] + forms = [] + for k, v in d.items(): + o = v.get_object() + try: + content: Any = None + if ( + to_delete & ObjectDeletionFlag.XOBJECT_IMAGES + and o["/Subtype"] == "/Image" + ): + content = NullObject() # to delete the image keeping the entry + images.append(k) + if o["/Subtype"] == "/Form": + forms.append(k) + if isinstance(o, ContentStream): + content = o + else: + content = ContentStream(o, self) + content.update( + { + k1: v1 + for k1, v1 in o.items() + if k1 not in ["/Length", "/Filter", "/DecodeParms"] + } + ) + try: + content.indirect_reference = o.indirect_reference + except AttributeError: # pragma: no cover + pass + stack.append(elt) + clean_forms(content, stack) # clean subforms + if content is not None: + if isinstance(v, IndirectObject): + self._objects[v.idnum - 1] = content + else: + # should only occur with pdf not respecting pdf spec + # where streams must be indirected. + d[k] = self._add_object(content) # pragma: no cover + except (TypeError, KeyError): + pass + for im in images: + del d[im] # for clean-up + if isinstance(elt, StreamObject): # for /Form + if not isinstance(elt, ContentStream): # pragma: no cover + e = ContentStream(elt, self) + e.update(elt.items()) + elt = e + clean(elt, images, forms) # clean the content + return images, forms + + if not isinstance(page, PageObject): + page = PageObject(self, page.indirect_reference) # pragma: no cover + if "/Contents" in page: + content = cast(ContentStream, page.get_contents()) + + images, forms = clean_forms(page, []) + + clean(content, images, forms) + page.replace_contents(content) + + def remove_images( + self, + to_delete: ImageType = ImageType.ALL, + ) -> None: + """ + Remove images from this output. + + Args: + to_delete : The type of images to be deleted + (default = all images types) + """ + if isinstance(to_delete, bool): + to_delete = ImageType.ALL + i = ( + ( + ObjectDeletionFlag.XOBJECT_IMAGES + if to_delete & ImageType.XOBJECT_IMAGES + else ObjectDeletionFlag.NONE + ) + | ( + ObjectDeletionFlag.INLINE_IMAGES + if to_delete & ImageType.INLINE_IMAGES + else ObjectDeletionFlag.NONE + ) + | ( + ObjectDeletionFlag.DRAWING_IMAGES + if to_delete & ImageType.DRAWING_IMAGES + else ObjectDeletionFlag.NONE + ) + ) + for page in self.pages: + self.remove_objects_from_page(page, i) + + def remove_text(self) -> None: + """Remove text from this output.""" + for page in self.pages: + self.remove_objects_from_page(page, ObjectDeletionFlag.TEXT) + + def add_uri( + self, + page_number: int, + uri: str, + rect: RectangleObject, + border: Optional[ArrayObject] = None, + ) -> None: + """ + Add an URI from a rectangular area to the specified page. + + Args: + page_number: index of the page on which to place the URI action. + uri: URI of resource to link to. + rect: :class:`RectangleObject` or + array of four integers specifying the clickable rectangular area + ``[xLL, yLL, xUR, yUR]``, or string in the form + ``"[ xLL yLL xUR yUR ]"``. + border: if provided, an array describing border-drawing + properties. See the PDF spec for details. No border will be + drawn if this argument is omitted. + """ + page_link = self.get_object(self._pages)[PA.KIDS][page_number] # type: ignore + page_ref = cast(Dict[str, Any], self.get_object(page_link)) + + border_arr: BorderArrayType + if border is not None: + border_arr = [NumberObject(n) for n in border[:3]] + if len(border) == 4: + dash_pattern = ArrayObject([NumberObject(n) for n in border[3]]) + border_arr.append(dash_pattern) + else: + border_arr = [NumberObject(2), NumberObject(2), NumberObject(2)] + + if isinstance(rect, str): + rect = NumberObject(rect) + elif isinstance(rect, RectangleObject): + pass + else: + rect = RectangleObject(rect) + + lnk2 = DictionaryObject() + lnk2.update( + { + NameObject("/S"): NameObject("/URI"), + NameObject("/URI"): TextStringObject(uri), + } + ) + lnk = DictionaryObject() + lnk.update( + { + NameObject(AA.Type): NameObject("/Annot"), + NameObject(AA.Subtype): NameObject("/Link"), + NameObject(AA.P): page_link, + NameObject(AA.Rect): rect, + NameObject("/H"): NameObject("/I"), + NameObject(AA.Border): ArrayObject(border_arr), + NameObject("/A"): lnk2, + } + ) + lnk_ref = self._add_object(lnk) + + if PG.ANNOTS in page_ref: + page_ref[PG.ANNOTS].append(lnk_ref) + else: + page_ref[NameObject(PG.ANNOTS)] = ArrayObject([lnk_ref]) + + _valid_layouts = ( + "/NoLayout", + "/SinglePage", + "/OneColumn", + "/TwoColumnLeft", + "/TwoColumnRight", + "/TwoPageLeft", + "/TwoPageRight", + ) + + def _get_page_layout(self) -> Optional[LayoutType]: + try: + return cast(LayoutType, self._root_object["/PageLayout"]) + except KeyError: + return None + + def _set_page_layout(self, layout: Union[NameObject, LayoutType]) -> None: + """ + Set the page layout. + + Args: + layout: The page layout to be used. + + .. list-table:: Valid ``layout`` arguments + :widths: 50 200 + + * - /NoLayout + - Layout explicitly not specified + * - /SinglePage + - Show one page at a time + * - /OneColumn + - Show one column at a time + * - /TwoColumnLeft + - Show pages in two columns, odd-numbered pages on the left + * - /TwoColumnRight + - Show pages in two columns, odd-numbered pages on the right + * - /TwoPageLeft + - Show two pages at a time, odd-numbered pages on the left + * - /TwoPageRight + - Show two pages at a time, odd-numbered pages on the right + """ + if not isinstance(layout, NameObject): + if layout not in self._valid_layouts: + logger_warning( + f"Layout should be one of: {'', ''.join(self._valid_layouts)}", + __name__, + ) + layout = NameObject(layout) + self._root_object.update({NameObject("/PageLayout"): layout}) + + def set_page_layout(self, layout: LayoutType) -> None: + """ + Set the page layout. + + Args: + layout: The page layout to be used + + .. list-table:: Valid ``layout`` arguments + :widths: 50 200 + + * - /NoLayout + - Layout explicitly not specified + * - /SinglePage + - Show one page at a time + * - /OneColumn + - Show one column at a time + * - /TwoColumnLeft + - Show pages in two columns, odd-numbered pages on the left + * - /TwoColumnRight + - Show pages in two columns, odd-numbered pages on the right + * - /TwoPageLeft + - Show two pages at a time, odd-numbered pages on the left + * - /TwoPageRight + - Show two pages at a time, odd-numbered pages on the right + """ + self._set_page_layout(layout) + + @property + def page_layout(self) -> Optional[LayoutType]: + """ + Page layout property. + + .. list-table:: Valid ``layout`` values + :widths: 50 200 + + * - /NoLayout + - Layout explicitly not specified + * - /SinglePage + - Show one page at a time + * - /OneColumn + - Show one column at a time + * - /TwoColumnLeft + - Show pages in two columns, odd-numbered pages on the left + * - /TwoColumnRight + - Show pages in two columns, odd-numbered pages on the right + * - /TwoPageLeft + - Show two pages at a time, odd-numbered pages on the left + * - /TwoPageRight + - Show two pages at a time, odd-numbered pages on the right + """ + return self._get_page_layout() + + @page_layout.setter + def page_layout(self, layout: LayoutType) -> None: + self._set_page_layout(layout) + + _valid_modes = ( + "/UseNone", + "/UseOutlines", + "/UseThumbs", + "/FullScreen", + "/UseOC", + "/UseAttachments", + ) + + def _get_page_mode(self) -> Optional[PagemodeType]: + try: + return cast(PagemodeType, self._root_object["/PageMode"]) + except KeyError: + return None + + @property + def page_mode(self) -> Optional[PagemodeType]: + """ + Page mode property. + + .. list-table:: Valid ``mode`` values + :widths: 50 200 + + * - /UseNone + - Do not show outline or thumbnails panels + * - /UseOutlines + - Show outline (aka bookmarks) panel + * - /UseThumbs + - Show page thumbnails panel + * - /FullScreen + - Fullscreen view + * - /UseOC + - Show Optional Content Group (OCG) panel + * - /UseAttachments + - Show attachments panel + """ + return self._get_page_mode() + + @page_mode.setter + def page_mode(self, mode: PagemodeType) -> None: + if isinstance(mode, NameObject): + mode_name: NameObject = mode + else: + if mode not in self._valid_modes: + logger_warning( + f"Mode should be one of: {', '.join(self._valid_modes)}", __name__ + ) + mode_name = NameObject(mode) + self._root_object.update({NameObject("/PageMode"): mode_name}) + + def add_annotation( + self, + page_number: Union[int, PageObject], + annotation: Dict[str, Any], + ) -> DictionaryObject: + """ + Add a single annotation to the page. + The added annotation must be a new annotation. + It cannot be recycled. + + Args: + page_number: PageObject or page index. + annotation: Annotation to be added (created with annotation). + + Returns: + The inserted object. + This can be used for popup creation, for example. + """ + page = page_number + if isinstance(page, int): + page = self.pages[page] + elif not isinstance(page, PageObject): + raise TypeError("page: invalid type") + + to_add = cast(DictionaryObject, _pdf_objectify(annotation)) + to_add[NameObject("/P")] = page.indirect_reference + + if page.annotations is None: + page[NameObject("/Annots")] = ArrayObject() + assert page.annotations is not None + + # Internal link annotations need the correct object type for the + # destination + if to_add.get("/Subtype") == "/Link" and "/Dest" in to_add: + tmp = cast(Dict[Any, Any], to_add[NameObject("/Dest")]) + dest = Destination( + NameObject("/LinkName"), + tmp["target_page_index"], + Fit( + fit_type=tmp["fit"], fit_args=dict(tmp)["fit_args"] + ), # I have no clue why this dict-hack is necessary + ) + to_add[NameObject("/Dest")] = dest.dest_array + + page.annotations.append(self._add_object(to_add)) + + if to_add.get("/Subtype") == "/Popup" and NameObject("/Parent") in to_add: + cast(DictionaryObject, to_add["/Parent"].get_object())[ + NameObject("/Popup") + ] = to_add.indirect_reference + + return to_add + + def clean_page(self, page: Union[PageObject, IndirectObject]) -> PageObject: + """ + Perform some clean up in the page. + Currently: convert NameObject named destination to TextStringObject + (required for names/dests list) + + Args: + page: + + Returns: + The cleaned PageObject + """ + page = cast("PageObject", page.get_object()) + for a in page.get("/Annots", []): + a_obj = a.get_object() + d = a_obj.get("/Dest", None) + act = a_obj.get("/A", None) + if isinstance(d, NameObject): + a_obj[NameObject("/Dest")] = TextStringObject(d) + elif act is not None: + act = act.get_object() + d = act.get("/D", None) + if isinstance(d, NameObject): + act[NameObject("/D")] = TextStringObject(d) + return page + + def _create_stream( + self, fileobj: Union[Path, StrByteType, PdfReader] + ) -> Tuple[IOBase, Optional[Encryption]]: + # If the fileobj parameter is a string, assume it is a path + # and create a file object at that location. If it is a file, + # copy the file's contents into a BytesIO stream object; if + # it is a PdfReader, copy that reader's stream into a + # BytesIO stream. + # If fileobj is none of the above types, it is not modified + encryption_obj = None + stream: IOBase + if isinstance(fileobj, (str, Path)): + with FileIO(fileobj, "rb") as f: + stream = BytesIO(f.read()) + elif isinstance(fileobj, PdfReader): + if fileobj._encryption: + encryption_obj = fileobj._encryption + orig_tell = fileobj.stream.tell() + fileobj.stream.seek(0) + stream = BytesIO(fileobj.stream.read()) + + # reset the stream to its original location + fileobj.stream.seek(orig_tell) + elif hasattr(fileobj, "seek") and hasattr(fileobj, "read"): + fileobj.seek(0) + filecontent = fileobj.read() + stream = BytesIO(filecontent) + else: + raise NotImplementedError( + "Merging requires an object that PdfReader can parse. " + "Typically, that is a Path or a string representing a Path, " + "a file object, or an object implementing .seek and .read. " + "Passing a PdfReader directly works as well." + ) + return stream, encryption_obj + + def append( + self, + fileobj: Union[StrByteType, PdfReader, Path], + outline_item: Union[ + str, None, PageRange, Tuple[int, int], Tuple[int, int, int], List[int] + ] = None, + pages: Union[ + None, + PageRange, + Tuple[int, int], + Tuple[int, int, int], + List[int], + List[PageObject], + ] = None, + import_outline: bool = True, + excluded_fields: Optional[Union[List[str], Tuple[str, ...]]] = None, + ) -> None: + """ + Identical to the :meth:`merge()` method, but assumes you want to + concatenate all pages onto the end of the file instead of specifying a + position. + + Args: + fileobj: A File Object or an object that supports the standard + read and seek methods similar to a File Object. Could also be a + string representing a path to a PDF file. + outline_item: Optionally, you may specify a string to build an + outline (aka 'bookmark') to identify the beginning of the + included file. + pages: Can be a :class:`PageRange` + or a ``(start, stop[, step])`` tuple + or a list of pages to be processed + to merge only the specified range of pages from the source + document into the output document. + import_outline: You may prevent the source document's + outline (collection of outline items, previously referred to as + 'bookmarks') from being imported by specifying this as ``False``. + excluded_fields: Provide the list of fields/keys to be ignored + if ``/Annots`` is part of the list, the annotation will be ignored + if ``/B`` is part of the list, the articles will be ignored + """ + if excluded_fields is None: + excluded_fields = () + if isinstance(outline_item, (tuple, list, PageRange)): + if isinstance(pages, bool): + if not isinstance(import_outline, bool): + excluded_fields = import_outline + import_outline = pages + pages = outline_item + self.merge( + None, + fileobj, + None, + pages, + import_outline, + excluded_fields, + ) + else: # if isinstance(outline_item,str): + self.merge( + None, + fileobj, + outline_item, + pages, + import_outline, + excluded_fields, + ) + + def merge( + self, + position: Optional[int], + fileobj: Union[Path, StrByteType, PdfReader], + outline_item: Optional[str] = None, + pages: Optional[Union[PageRangeSpec, List[PageObject]]] = None, + import_outline: bool = True, + excluded_fields: Optional[Union[List[str], Tuple[str, ...]]] = (), + ) -> None: + """ + Merge the pages from the given file into the output file at the + specified page number. + + Args: + position: The *page number* to insert this file. File will + be inserted after the given number. + fileobj: A File Object or an object that supports the standard + read and seek methods similar to a File Object. Could also be a + string representing a path to a PDF file. + outline_item: Optionally, you may specify a string to build an outline + (aka 'bookmark') to identify the + beginning of the included file. + pages: can be a :class:`PageRange` + or a ``(start, stop[, step])`` tuple + or a list of pages to be processed + to merge only the specified range of pages from the source + document into the output document. + import_outline: You may prevent the source document's + outline (collection of outline items, previously referred to as + 'bookmarks') from being imported by specifying this as ``False``. + excluded_fields: provide the list of fields/keys to be ignored + if ``/Annots`` is part of the list, the annotation will be ignored + if ``/B`` is part of the list, the articles will be ignored + + Raises: + TypeError: The pages attribute is not configured properly + """ + if isinstance(fileobj, PdfDocCommon): + reader = fileobj + else: + stream, encryption_obj = self._create_stream(fileobj) + # Create a new PdfReader instance using the stream + # (either file or BytesIO or StringIO) created above + reader = PdfReader(stream, strict=False) # type: ignore[arg-type] + + if excluded_fields is None: + excluded_fields = () + # Find the range of pages to merge. + if pages is None: + pages = list(range(len(reader.pages))) + elif isinstance(pages, PageRange): + pages = list(range(*pages.indices(len(reader.pages)))) + elif isinstance(pages, list): + pass # keep unchanged + elif isinstance(pages, tuple) and len(pages) <= 3: + pages = list(range(*pages)) + elif not isinstance(pages, tuple): + raise TypeError( + '"pages" must be a tuple of (start, stop[, step]) or a list' + ) + + srcpages = {} + for page in pages: + if isinstance(page, PageObject): + pg = page + else: + pg = reader.pages[page] + assert pg.indirect_reference is not None + if position is None: + # numbers in the exclude list identifies that the exclusion is + # only applicable to 1st level of cloning + srcpages[pg.indirect_reference.idnum] = self.add_page( + pg, list(excluded_fields) + [1, "/B", 1, "/Annots"] # type: ignore + ) + else: + srcpages[pg.indirect_reference.idnum] = self.insert_page( + pg, position, list(excluded_fields) + [1, "/B", 1, "/Annots"] # type: ignore + ) + position += 1 + srcpages[pg.indirect_reference.idnum].original_page = pg + + reader._namedDests = ( + reader.named_destinations + ) # need for the outline processing below + for dest in reader._namedDests.values(): + arr = dest.dest_array + if "/Names" in self._root_object and dest["/Title"] in cast( + List[Any], + cast( + DictionaryObject, + cast(DictionaryObject, self._root_object["/Names"])["/Dests"], + )["/Names"], + ): + # already exists : should not duplicate it + pass + elif isinstance(dest["/Page"], NullObject): + pass + elif isinstance(dest["/Page"], int): + # the page reference is a page number normally not a PDF Reference + # page numbers as int are normally accepted only in external goto + p = reader.pages[dest["/Page"]] + assert p.indirect_reference is not None + try: + arr[NumberObject(0)] = NumberObject( + srcpages[p.indirect_reference.idnum].page_number + ) + self.add_named_destination_array(dest["/Title"], arr) + except KeyError: + pass + elif dest["/Page"].indirect_reference.idnum in srcpages: + arr[NumberObject(0)] = srcpages[ + dest["/Page"].indirect_reference.idnum + ].indirect_reference + self.add_named_destination_array(dest["/Title"], arr) + + outline_item_typ: TreeObject + if outline_item is not None: + outline_item_typ = cast( + "TreeObject", + self.add_outline_item( + TextStringObject(outline_item), + next(iter(srcpages.values())).indirect_reference, + fit=PAGE_FIT, + ).get_object(), + ) + else: + outline_item_typ = self.get_outline_root() + + _ro = reader.root_object + if import_outline and CO.OUTLINES in _ro: + outline = self._get_filtered_outline( + _ro.get(CO.OUTLINES, None), srcpages, reader + ) + self._insert_filtered_outline( + outline, outline_item_typ, None + ) # TODO : use before parameter + + if "/Annots" not in excluded_fields: + for pag in srcpages.values(): + lst = self._insert_filtered_annotations( + pag.original_page.get("/Annots", ()), pag, srcpages, reader + ) + if len(lst) > 0: + pag[NameObject("/Annots")] = lst + self.clean_page(pag) + + if "/AcroForm" in _ro and _ro["/AcroForm"] is not None: + if "/AcroForm" not in self._root_object: + self._root_object[NameObject("/AcroForm")] = self._add_object( + cast( + DictionaryObject, + reader.root_object["/AcroForm"], + ).clone(self, False, ("/Fields",)) + ) + arr = ArrayObject() + else: + arr = cast( + ArrayObject, + cast(DictionaryObject, self._root_object["/AcroForm"])["/Fields"], + ) + trslat = self._id_translated[id(reader)] + try: + for f in reader.root_object["/AcroForm"]["/Fields"]: # type: ignore + try: + ind = IndirectObject(trslat[f.idnum], 0, self) + if ind not in arr: + arr.append(ind) + except KeyError: + # for trslat[] which mean the field has not be copied + # through the page + pass + except KeyError: # for /Acroform or /Fields are not existing + arr = self._add_object(ArrayObject()) + cast(DictionaryObject, self._root_object["/AcroForm"])[ + NameObject("/Fields") + ] = arr + + if "/B" not in excluded_fields: + self.add_filtered_articles("", srcpages, reader) + + def _add_articles_thread( + self, + thread: DictionaryObject, # thread entry from the reader's array of threads + pages: Dict[int, PageObject], + reader: PdfReader, + ) -> IndirectObject: + """ + Clone the thread with only the applicable articles. + + Args: + thread: + pages: + reader: + + Returns: + The added thread as an indirect reference + """ + nthread = thread.clone( + self, force_duplicate=True, ignore_fields=("/F",) + ) # use of clone to keep link between reader and writer + self.threads.append(nthread.indirect_reference) + first_article = cast("DictionaryObject", thread["/F"]) + current_article: Optional[DictionaryObject] = first_article + new_article: Optional[DictionaryObject] = None + while current_article is not None: + pag = self._get_cloned_page( + cast("PageObject", current_article["/P"]), pages, reader + ) + if pag is not None: + if new_article is None: + new_article = cast( + "DictionaryObject", + self._add_object(DictionaryObject()).get_object(), + ) + new_first = new_article + nthread[NameObject("/F")] = new_article.indirect_reference + else: + new_article2 = cast( + "DictionaryObject", + self._add_object( + DictionaryObject( + {NameObject("/V"): new_article.indirect_reference} + ) + ).get_object(), + ) + new_article[NameObject("/N")] = new_article2.indirect_reference + new_article = new_article2 + new_article[NameObject("/P")] = pag + new_article[NameObject("/T")] = nthread.indirect_reference + new_article[NameObject("/R")] = current_article["/R"] + pag_obj = cast("PageObject", pag.get_object()) + if "/B" not in pag_obj: + pag_obj[NameObject("/B")] = ArrayObject() + cast("ArrayObject", pag_obj["/B"]).append( + new_article.indirect_reference + ) + current_article = cast("DictionaryObject", current_article["/N"]) + if current_article == first_article: + new_article[NameObject("/N")] = new_first.indirect_reference # type: ignore + new_first[NameObject("/V")] = new_article.indirect_reference # type: ignore + current_article = None + assert nthread.indirect_reference is not None + return nthread.indirect_reference + + def add_filtered_articles( + self, + fltr: Union[ + Pattern[Any], str + ], # thread entry from the reader's array of threads + pages: Dict[int, PageObject], + reader: PdfReader, + ) -> None: + """ + Add articles matching the defined criteria. + + Args: + fltr: + pages: + reader: + """ + if isinstance(fltr, str): + fltr = re.compile(fltr) + elif not isinstance(fltr, Pattern): + fltr = re.compile("") + for p in pages.values(): + pp = p.original_page + for a in pp.get("/B", ()): + thr = a.get_object().get("/T") + if thr is None: + continue + else: + thr = thr.get_object() + if thr.indirect_reference.idnum not in self._id_translated[ + id(reader) + ] and fltr.search((thr["/I"] if "/I" in thr else {}).get("/Title", "")): + self._add_articles_thread(thr, pages, reader) + + def _get_cloned_page( + self, + page: Union[None, IndirectObject, PageObject, NullObject], + pages: Dict[int, PageObject], + reader: PdfReader, + ) -> Optional[IndirectObject]: + if isinstance(page, NullObject): + return None + elif isinstance(page, DictionaryObject) and page.get("/Type", "") == "/Page": + _i = page.indirect_reference + elif isinstance(page, IndirectObject): + _i = page + try: + return pages[_i.idnum].indirect_reference # type: ignore + except Exception: + return None + + def _insert_filtered_annotations( + self, + annots: Union[IndirectObject, List[DictionaryObject]], + page: PageObject, + pages: Dict[int, PageObject], + reader: PdfReader, + ) -> List[Destination]: + outlist = ArrayObject() + if isinstance(annots, IndirectObject): + annots = cast("List[Any]", annots.get_object()) + for an in annots: + ano = cast("DictionaryObject", an.get_object()) + if ( + ano["/Subtype"] != "/Link" + or "/A" not in ano + or cast("DictionaryObject", ano["/A"])["/S"] != "/GoTo" + or "/Dest" in ano + ): + if "/Dest" not in ano: + outlist.append(self._add_object(ano.clone(self))) + else: + d = ano["/Dest"] + if isinstance(d, str): + # it is a named dest + if str(d) in self.get_named_dest_root(): + outlist.append(ano.clone(self).indirect_reference) + else: + d = cast("ArrayObject", d) + p = self._get_cloned_page(d[0], pages, reader) + if p is not None: + anc = ano.clone(self, ignore_fields=("/Dest",)) + anc[NameObject("/Dest")] = ArrayObject([p] + d[1:]) + outlist.append(self._add_object(anc)) + else: + d = cast("DictionaryObject", ano["/A"])["/D"] + if isinstance(d, str): + # it is a named dest + if str(d) in self.get_named_dest_root(): + outlist.append(ano.clone(self).indirect_reference) + else: + d = cast("ArrayObject", d) + p = self._get_cloned_page(d[0], pages, reader) + if p is not None: + anc = ano.clone(self, ignore_fields=("/D",)) + cast("DictionaryObject", anc["/A"])[ + NameObject("/D") + ] = ArrayObject([p] + d[1:]) + outlist.append(self._add_object(anc)) + return outlist + + def _get_filtered_outline( + self, + node: Any, + pages: Dict[int, PageObject], + reader: PdfReader, + ) -> List[Destination]: + """ + Extract outline item entries that are part of the specified page set. + + Args: + node: + pages: + reader: + + Returns: + A list of destination objects. + """ + new_outline = [] + if node is None: + node = NullObject() + node = node.get_object() + if is_null_or_none(node): + node = DictionaryObject() + if node.get("/Type", "") == "/Outlines" or "/Title" not in node: + node = node.get("/First", None) + if node is not None: + node = node.get_object() + new_outline += self._get_filtered_outline(node, pages, reader) + else: + v: Union[None, IndirectObject, NullObject] + while node is not None: + node = node.get_object() + o = cast("Destination", reader._build_outline_item(node)) + v = self._get_cloned_page(cast("PageObject", o["/Page"]), pages, reader) + if v is None: + v = NullObject() + o[NameObject("/Page")] = v + if "/First" in node: + o._filtered_children = self._get_filtered_outline( + node["/First"], pages, reader + ) + else: + o._filtered_children = [] + if ( + not isinstance(o["/Page"], NullObject) + or len(o._filtered_children) > 0 + ): + new_outline.append(o) + node = node.get("/Next", None) + return new_outline + + def _clone_outline(self, dest: Destination) -> TreeObject: + n_ol = TreeObject() + self._add_object(n_ol) + n_ol[NameObject("/Title")] = TextStringObject(dest["/Title"]) + if not isinstance(dest["/Page"], NullObject): + if dest.node is not None and "/A" in dest.node: + n_ol[NameObject("/A")] = dest.node["/A"].clone(self) + else: + n_ol[NameObject("/Dest")] = dest.dest_array + # TODO: /SE + if dest.node is not None: + n_ol[NameObject("/F")] = NumberObject(dest.node.get("/F", 0)) + n_ol[NameObject("/C")] = ArrayObject( + dest.node.get( + "/C", [FloatObject(0.0), FloatObject(0.0), FloatObject(0.0)] + ) + ) + return n_ol + + def _insert_filtered_outline( + self, + outlines: List[Destination], + parent: Union[TreeObject, IndirectObject], + before: Union[None, TreeObject, IndirectObject] = None, + ) -> None: + for dest in outlines: + # TODO : can be improved to keep A and SE entries (ignored for the moment) + # with np=self.add_outline_item_destination(dest,parent,before) + if dest.get("/Type", "") == "/Outlines" or "/Title" not in dest: + np = parent + else: + np = self._clone_outline(dest) + cast(TreeObject, parent.get_object()).insert_child(np, before, self) + self._insert_filtered_outline(dest._filtered_children, np, None) + + def close(self) -> None: + """Implemented for API harmonization.""" + return + + def find_outline_item( + self, + outline_item: Dict[str, Any], + root: Optional[OutlineType] = None, + ) -> Optional[List[int]]: + if root is None: + o = self.get_outline_root() + else: + o = cast("TreeObject", root) + + i = 0 + while o is not None: + if ( + o.indirect_reference == outline_item + or o.get("/Title", None) == outline_item + ): + return [i] + elif "/First" in o: + res = self.find_outline_item( + outline_item, cast(OutlineType, o["/First"]) + ) + if res: + return ([i] if "/Title" in o else []) + res + if "/Next" in o: + i += 1 + o = cast(TreeObject, o["/Next"]) + else: + return None + + def find_bookmark( + self, + outline_item: Dict[str, Any], + root: Optional[OutlineType] = None, + ) -> None: # deprecated + """ + .. deprecated:: 2.9.0 + Use :meth:`find_outline_item` instead. + """ + deprecation_with_replacement("find_bookmark", "find_outline_item", "5.0.0") + + def reset_translation( + self, reader: Union[None, PdfReader, IndirectObject] = None + ) -> None: + """ + Reset the translation table between reader and the writer object. + + Late cloning will create new independent objects. + + Args: + reader: PdfReader or IndirectObject referencing a PdfReader object. + if set to None or omitted, all tables will be reset. + """ + if reader is None: + self._id_translated = {} + elif isinstance(reader, PdfReader): + try: + del self._id_translated[id(reader)] + except Exception: + pass + elif isinstance(reader, IndirectObject): + try: + del self._id_translated[id(reader.pdf)] + except Exception: + pass + else: + raise Exception("invalid parameter {reader}") + + def set_page_label( + self, + page_index_from: int, + page_index_to: int, + style: Optional[PageLabelStyle] = None, + prefix: Optional[str] = None, + start: Optional[int] = 0, + ) -> None: + """ + Set a page label to a range of pages. + + Page indexes must be given starting from 0. + Labels must have a style, a prefix or both. + If to a range is not assigned any page label a decimal label starting from 1 is applied. + + Args: + page_index_from: page index of the beginning of the range starting from 0 + page_index_to: page index of the beginning of the range starting from 0 + style: The numbering style to be used for the numeric portion of each page label: + + * ``/D`` Decimal arabic numerals + * ``/R`` Uppercase roman numerals + * ``/r`` Lowercase roman numerals + * ``/A`` Uppercase letters (A to Z for the first 26 pages, + AA to ZZ for the next 26, and so on) + * ``/a`` Lowercase letters (a to z for the first 26 pages, + aa to zz for the next 26, and so on) + + prefix: The label prefix for page labels in this range. + start: The value of the numeric portion for the first page label + in the range. + Subsequent pages are numbered sequentially from this value, + which must be greater than or equal to 1. + Default value: 1. + """ + if style is None and prefix is None: + raise ValueError("at least one between style and prefix must be given") + if page_index_from < 0: + raise ValueError("page_index_from must be equal or greater then 0") + if page_index_to < page_index_from: + raise ValueError( + "page_index_to must be equal or greater then page_index_from" + ) + if page_index_to >= len(self.pages): + raise ValueError("page_index_to exceeds number of pages") + if start is not None and start != 0 and start < 1: + raise ValueError("if given, start must be equal or greater than one") + + self._set_page_label(page_index_from, page_index_to, style, prefix, start) + + def _set_page_label( + self, + page_index_from: int, + page_index_to: int, + style: Optional[PageLabelStyle] = None, + prefix: Optional[str] = None, + start: Optional[int] = 0, + ) -> None: + """ + Set a page label to a range of pages. + + Page indexes must be given + starting from 0. Labels must have a style, a prefix or both. If to a + range is not assigned any page label a decimal label starting from 1 is + applied. + + Args: + page_index_from: page index of the beginning of the range starting from 0 + page_index_to: page index of the beginning of the range starting from 0 + style: The numbering style to be used for the numeric portion of each page label: + /D Decimal arabic numerals + /R Uppercase roman numerals + /r Lowercase roman numerals + /A Uppercase letters (A to Z for the first 26 pages, + AA to ZZ for the next 26, and so on) + /a Lowercase letters (a to z for the first 26 pages, + aa to zz for the next 26, and so on) + prefix: The label prefix for page labels in this range. + start: The value of the numeric portion for the first page label + in the range. + Subsequent pages are numbered sequentially from this value, + which must be greater than or equal to 1. Default value: 1. + """ + default_page_label = DictionaryObject() + default_page_label[NameObject("/S")] = NameObject("/D") + + new_page_label = DictionaryObject() + if style is not None: + new_page_label[NameObject("/S")] = NameObject(style) + if prefix is not None: + new_page_label[NameObject("/P")] = TextStringObject(prefix) + if start != 0: + new_page_label[NameObject("/St")] = NumberObject(start) + + if NameObject(CatalogDictionary.PAGE_LABELS) not in self._root_object: + nums = ArrayObject() + nums_insert(NumberObject(0), default_page_label, nums) + page_labels = TreeObject() + page_labels[NameObject("/Nums")] = nums + self._root_object[NameObject(CatalogDictionary.PAGE_LABELS)] = page_labels + + page_labels = cast( + TreeObject, self._root_object[NameObject(CatalogDictionary.PAGE_LABELS)] + ) + nums = cast(ArrayObject, page_labels[NameObject("/Nums")]) + + nums_insert(NumberObject(page_index_from), new_page_label, nums) + nums_clear_range(NumberObject(page_index_from), page_index_to, nums) + next_label_pos, *_ = nums_next(NumberObject(page_index_from), nums) + if next_label_pos != page_index_to + 1 and page_index_to + 1 < len(self.pages): + nums_insert(NumberObject(page_index_to + 1), default_page_label, nums) + + page_labels[NameObject("/Nums")] = nums + self._root_object[NameObject(CatalogDictionary.PAGE_LABELS)] = page_labels + + +def _pdf_objectify(obj: Union[Dict[str, Any], str, int, List[Any]]) -> PdfObject: + if isinstance(obj, PdfObject): + return obj + if isinstance(obj, dict): + to_add = DictionaryObject() + for key, value in obj.items(): + name_key = NameObject(key) + casted_value = _pdf_objectify(value) + to_add[name_key] = casted_value + return to_add + elif isinstance(obj, list): + return ArrayObject(_pdf_objectify(el) for el in obj) + elif isinstance(obj, str): + if obj.startswith("/"): + return NameObject(obj) + else: + return TextStringObject(obj) + elif isinstance(obj, (int, float)): + return FloatObject(obj) + else: + raise NotImplementedError( + f"type(obj)={type(obj)} could not be casted to PdfObject" + ) + + +def _create_outline_item( + action_ref: Union[None, IndirectObject], + title: str, + color: Union[Tuple[float, float, float], str, None], + italic: bool, + bold: bool, +) -> TreeObject: + outline_item = TreeObject() + if action_ref is not None: + outline_item[NameObject("/A")] = action_ref + outline_item.update( + { + NameObject("/Title"): create_string_object(title), + } + ) + if color: + if isinstance(color, str): + color = hex_to_rgb(color) + outline_item.update( + {NameObject("/C"): ArrayObject([FloatObject(c) for c in color])} + ) + if italic or bold: + format_flag = 0 + if italic: + format_flag += 1 + if bold: + format_flag += 2 + outline_item.update({NameObject("/F"): NumberObject(format_flag)}) + return outline_item diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_xobj_image_helpers.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_xobj_image_helpers.py new file mode 100644 index 00000000..d870b158 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/_xobj_image_helpers.py @@ -0,0 +1,305 @@ +"""Code in here is only used by pypdf.filters._xobj_to_image""" + +import sys +from io import BytesIO +from typing import Any, List, Literal, Tuple, Union, cast + +from ._utils import check_if_whitespace_only, logger_warning +from .constants import ColorSpaces +from .errors import EmptyImageDataError, PdfReadError +from .generic import ( + ArrayObject, + DecodedStreamObject, + EncodedStreamObject, + IndirectObject, + NullObject, +) + +if sys.version_info[:2] >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + + +try: + from PIL import Image, UnidentifiedImageError # noqa: F401 +except ImportError: + raise ImportError( + "pillow is required to do image extraction. " + "It can be installed via 'pip install pypdf[image]'" + ) + +mode_str_type: TypeAlias = Literal[ + "", "1", "RGB", "2bits", "4bits", "P", "L", "RGBA", "CMYK" +] + +MAX_IMAGE_MODE_NESTING_DEPTH: int = 10 + + +def _get_imagemode( + color_space: Union[str, List[Any], Any], + color_components: int, + prev_mode: mode_str_type, + depth: int = 0, +) -> Tuple[mode_str_type, bool]: + """ + Returns + Image mode not taking into account mask(transparency) + ColorInversion is required (like for some DeviceCMYK) + """ + if depth > MAX_IMAGE_MODE_NESTING_DEPTH: + raise PdfReadError( + "Color spaces nested too deep. If required, consider increasing MAX_IMAGE_MODE_NESTING_DEPTH." + ) + if isinstance(color_space, NullObject): + return "", False + if isinstance(color_space, str): + pass + elif not isinstance(color_space, list): + raise PdfReadError( + "Cannot interpret colorspace", color_space + ) # pragma: no cover + elif color_space[0].startswith("/Cal"): # /CalRGB and /CalGray + color_space = "/Device" + color_space[0][4:] + elif color_space[0] == "/ICCBased": + icc_profile = color_space[1].get_object() + color_components = cast(int, icc_profile["/N"]) + color_space = icc_profile.get("/Alternate", "") + elif color_space[0] == "/Indexed": + color_space = color_space[1].get_object() + mode2, invert_color = _get_imagemode( + color_space, color_components, prev_mode, depth + 1 + ) + if mode2 in ("RGB", "CMYK"): + mode2 = "P" + return mode2, invert_color + elif color_space[0] == "/Separation": + color_space = color_space[2] + if isinstance(color_space, IndirectObject): + color_space = color_space.get_object() + mode2, invert_color = _get_imagemode( + color_space, color_components, prev_mode, depth + 1 + ) + return mode2, True + elif color_space[0] == "/DeviceN": + original_color_space = color_space + color_components = len(color_space[1]) + color_space = color_space[2] + if isinstance(color_space, IndirectObject): # pragma: no cover + color_space = color_space.get_object() + if color_space == "/DeviceCMYK" and color_components == 1: + if original_color_space[1][0] != "/Black": + logger_warning( + f"Color {original_color_space[1][0]} converted to Gray. Please share PDF with pypdf dev team", + __name__, + ) + return "L", True + mode2, invert_color = _get_imagemode( + color_space, color_components, prev_mode, depth + 1 + ) + return mode2, invert_color + + mode_map = { + "1bit": "1", # pos [0] will be used for 1 bit + "/DeviceGray": "L", # must be in pos [1] + "palette": "P", # must be in pos [2] for color_components align. + "/DeviceRGB": "RGB", # must be in pos [3] + "/DeviceCMYK": "CMYK", # must be in pos [4] + "2bit": "2bits", # 2 bits images + "4bit": "4bits", # 4 bits + } + mode: mode_str_type = ( + mode_map.get(color_space) # type: ignore + or list(mode_map.values())[color_components] + or prev_mode + ) + return mode, mode == "CMYK" + + +def bits2byte(data: bytes, size: Tuple[int, int], bits: int) -> bytes: + mask = (1 << bits) - 1 + nbuff = bytearray(size[0] * size[1]) + by = 0 + bit = 8 - bits + for y in range(size[1]): + if bit != 8 - bits: + by += 1 + bit = 8 - bits + for x in range(size[0]): + nbuff[y * size[0] + x] = (data[by] >> bit) & mask + bit -= bits + if bit < 0: + by += 1 + bit = 8 - bits + return bytes(nbuff) + + +def _extended_image_frombytes( + mode: str, size: Tuple[int, int], data: bytes +) -> Image.Image: + try: + img = Image.frombytes(mode, size, data) + except ValueError as exc: + nb_pix = size[0] * size[1] + data_length = len(data) + if data_length == 0: + raise EmptyImageDataError( + "Data is 0 bytes, cannot process an image from empty data." + ) from exc + if data_length % nb_pix != 0: + raise exc + k = nb_pix * len(mode) / data_length + data = b"".join([bytes((x,) * int(k)) for x in data]) + img = Image.frombytes(mode, size, data) + return img + + +def _handle_flate( + size: Tuple[int, int], + data: bytes, + mode: mode_str_type, + color_space: str, + colors: int, + obj_as_text: str, +) -> Tuple[Image.Image, str, str, bool]: + """ + Process image encoded in flateEncode + Returns img, image_format, extension, color inversion + """ + extension = ".png" # mime_type = "image/png" + image_format = "PNG" + lookup: Any + base: Any + hival: Any + if isinstance(color_space, ArrayObject) and color_space[0] == "/Indexed": + color_space, base, hival, lookup = (value.get_object() for value in color_space) + if mode == "2bits": + mode = "P" + data = bits2byte(data, size, 2) + elif mode == "4bits": + mode = "P" + data = bits2byte(data, size, 4) + img = _extended_image_frombytes(mode, size, data) + if color_space == "/Indexed": + from .generic import TextStringObject + + if isinstance(lookup, (EncodedStreamObject, DecodedStreamObject)): + lookup = lookup.get_data() + if isinstance(lookup, TextStringObject): + lookup = lookup.original_bytes + if isinstance(lookup, str): + lookup = lookup.encode() + try: + nb, conv, mode = { # type: ignore + "1": (0, "", ""), + "L": (1, "P", "L"), + "P": (0, "", ""), + "RGB": (3, "P", "RGB"), + "CMYK": (4, "P", "CMYK"), + }[_get_imagemode(base, 0, "")[0]] + except KeyError: # pragma: no cover + logger_warning( + f"Base {base} not coded please share the pdf file with pypdf dev team", + __name__, + ) + lookup = None + else: + if img.mode == "1": + # Two values ("high" and "low"). + expected_count = 2 * nb + if len(lookup) != expected_count: + if len(lookup) < expected_count: + raise PdfReadError( + f"Not enough lookup values: Expected {expected_count}, got {len(lookup)}." + ) + if not check_if_whitespace_only(lookup[expected_count:]): + raise PdfReadError( + f"Too many lookup values: Expected {expected_count}, got {len(lookup)}." + ) + lookup = lookup[:expected_count] + colors_arr = [lookup[:nb], lookup[nb:]] + arr = b"".join( + [ + b"".join( + [ + colors_arr[1 if img.getpixel((x, y)) > 127 else 0] + for x in range(img.size[0]) + ] + ) + for y in range(img.size[1]) + ] + ) + img = Image.frombytes(mode, img.size, arr) + else: + img = img.convert(conv) + if len(lookup) != (hival + 1) * nb: + logger_warning(f"Invalid Lookup Table in {obj_as_text}", __name__) + lookup = None + elif mode == "L": + # gray lookup does not work : it is converted to a similar RGB lookup + lookup = b"".join([bytes([b, b, b]) for b in lookup]) + mode = "RGB" + # TODO : cf https://github.com/py-pdf/pypdf/pull/2039 + # this is a work around until PIL is able to process CMYK images + elif mode == "CMYK": + _rgb = [] + for _c, _m, _y, _k in ( + lookup[n : n + 4] for n in range(0, 4 * (len(lookup) // 4), 4) + ): + _r = int(255 * (1 - _c / 255) * (1 - _k / 255)) + _g = int(255 * (1 - _m / 255) * (1 - _k / 255)) + _b = int(255 * (1 - _y / 255) * (1 - _k / 255)) + _rgb.append(bytes((_r, _g, _b))) + lookup = b"".join(_rgb) + mode = "RGB" + if lookup is not None: + img.putpalette(lookup, rawmode=mode) + img = img.convert("L" if base == ColorSpaces.DEVICE_GRAY else "RGB") + elif not isinstance(color_space, NullObject) and color_space[0] == "/ICCBased": + # see Table 66 - Additional Entries Specific to an ICC Profile + # Stream Dictionary + mode2 = _get_imagemode(color_space, colors, mode)[0] + if mode != mode2: + img = Image.frombytes(mode2, size, data) # reloaded as mode may have change + if mode == "CMYK": + extension = ".tif" + image_format = "TIFF" + return img, image_format, extension, False + + +def _handle_jpx( + size: Tuple[int, int], + data: bytes, + mode: mode_str_type, + color_space: str, + colors: int, +) -> Tuple[Image.Image, str, str, bool]: + """ + Process image encoded in flateEncode + Returns img, image_format, extension, inversion + """ + extension = ".jp2" # mime_type = "image/x-jp2" + img1 = Image.open(BytesIO(data), formats=("JPEG2000",)) + mode, invert_color = _get_imagemode(color_space, colors, mode) + if mode == "": + mode = cast(mode_str_type, img1.mode) + invert_color = mode in ("CMYK",) + if img1.mode == "RGBA" and mode == "RGB": + mode = "RGBA" + # we need to convert to the good mode + if img1.mode == mode or {img1.mode, mode} == {"L", "P"}: # compare (unordered) sets + # L,P are indexed modes which should not be changed. + img = img1 + elif {img1.mode, mode} == {"RGBA", "CMYK"}: + # RGBA / CMYK are 4bytes encoding where + # the encoding should be corrected + img = Image.frombytes(mode, img1.size, img1.tobytes()) + else: # pragma: no cover + img = img1.convert(mode) + # for CMYK conversion : + # https://stcom/questions/38855022/conversion-from-cmyk-to-rgb-with-pillow-is-different-from-that-of-photoshop + # not implemented for the moment as I need to get properly the ICC + if img.mode == "CMYK": + img = img.convert("RGB") + image_format = "JPEG2000" + return img, image_format, extension, invert_color diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__init__.py new file mode 100644 index 00000000..208972d6 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__init__.py @@ -0,0 +1,45 @@ +""" +PDF specifies several annotation types which pypdf makes available here. + +The names of the annotations and their attributes do not reflect the names in +the specification in all cases. For example, the PDF standard defines a +'Square' annotation that does not actually need to be square. For this reason, +pypdf calls it 'Rectangle'. + +At their core, all annotation types are DictionaryObjects. That means if pypdf +does not implement a feature, users can easily extend the given functionality. +""" + + +from ._base import NO_FLAGS, AnnotationDictionary +from ._markup_annotations import ( + Ellipse, + FreeText, + Highlight, + Line, + MarkupAnnotation, + Polygon, + PolyLine, + Rectangle, + Text, +) +from ._non_markup_annotations import Link, Popup + +__all__ = [ + "NO_FLAGS", + # Export abstract base classes so that they are shown in the docs + "AnnotationDictionary", + "MarkupAnnotation", + # Markup annotations + "Ellipse", + "FreeText", + "Highlight", + "Line", + "Polygon", + "PolyLine", + "Rectangle", + "Text", + # Non-markup annotations + "Link", + "Popup", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..7e3e6a7a Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_base.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_base.cpython-38.pyc new file mode 100644 index 00000000..d734ea41 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_base.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_markup_annotations.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_markup_annotations.cpython-38.pyc new file mode 100644 index 00000000..d170c85f Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_markup_annotations.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_non_markup_annotations.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_non_markup_annotations.cpython-38.pyc new file mode 100644 index 00000000..8b4c7f5d Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/__pycache__/_non_markup_annotations.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_base.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_base.py new file mode 100644 index 00000000..2aff325a --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_base.py @@ -0,0 +1,27 @@ +from abc import ABC + +from ..constants import AnnotationFlag +from ..generic import NameObject, NumberObject +from ..generic._data_structures import DictionaryObject + + +class AnnotationDictionary(DictionaryObject, ABC): + def __init__(self) -> None: + from ..generic._base import NameObject + + # "rect" should not be added here as PolyLine can automatically set it + self[NameObject("/Type")] = NameObject("/Annot") + # The flags were NOT added to the constructor on purpose: We expect that + # most users don't want to change the default. If they do, they + # can use the property. The default is 0. + + @property + def flags(self) -> AnnotationFlag: + return self.get(NameObject("/F"), AnnotationFlag(0)) + + @flags.setter + def flags(self, value: AnnotationFlag) -> None: + self[NameObject("/F")] = NumberObject(value) + + +NO_FLAGS = AnnotationFlag(0) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_markup_annotations.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_markup_annotations.py new file mode 100644 index 00000000..580b8bf5 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_markup_annotations.py @@ -0,0 +1,308 @@ +import sys +from abc import ABC +from typing import Any, List, Optional, Tuple, Union + +from .._utils import deprecate_with_replacement +from ..constants import AnnotationFlag +from ..generic import ArrayObject, DictionaryObject +from ..generic._base import ( + BooleanObject, + FloatObject, + NameObject, + NumberObject, + TextStringObject, +) +from ..generic._rectangle import RectangleObject +from ..generic._utils import hex_to_rgb +from ._base import NO_FLAGS, AnnotationDictionary + +if sys.version_info[:2] >= (3, 10): + from typing import TypeAlias +else: + # PEP 613 introduced typing.TypeAlias with Python 3.10 + # For older Python versions, the backport typing_extensions is necessary: + from typing_extensions import TypeAlias + + +Vertex: TypeAlias = Tuple[float, float] + + +def _get_bounding_rectangle(vertices: List[Vertex]) -> RectangleObject: + x_min, y_min = vertices[0][0], vertices[0][1] + x_max, y_max = vertices[0][0], vertices[0][1] + for x, y in vertices: + x_min = min(x_min, x) + y_min = min(y_min, y) + x_max = max(x_max, x) + y_max = max(y_max, y) + rect = RectangleObject((x_min, y_min, x_max, y_max)) + return rect + + +class MarkupAnnotation(AnnotationDictionary, ABC): + """ + Base class for all markup annotations. + + Args: + title_bar: Text to be displayed in the title bar of the annotation; + by convention this is the name of the author + """ + + def __init__(self, *, title_bar: Optional[str] = None): + if title_bar is not None: + self[NameObject("/T")] = TextStringObject(title_bar) + + +class Text(MarkupAnnotation): + """ + A text annotation. + + Args: + rect: array of four integers ``[xLL, yLL, xUR, yUR]`` + specifying the clickable rectangular area + text: The text that is added to the document + open: + flags: + """ + + def __init__( + self, + *, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + text: str, + open: bool = False, + flags: int = NO_FLAGS, + **kwargs: Any, + ): + super().__init__(**kwargs) + self[NameObject("/Subtype")] = NameObject("/Text") + self[NameObject("/Rect")] = RectangleObject(rect) + self[NameObject("/Contents")] = TextStringObject(text) + self[NameObject("/Open")] = BooleanObject(open) + self[NameObject("/Flags")] = NumberObject(flags) + + +class FreeText(MarkupAnnotation): + """A FreeText annotation""" + + def __init__( + self, + *, + text: str, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + font: str = "Helvetica", + bold: bool = False, + italic: bool = False, + font_size: str = "14pt", + font_color: str = "000000", + border_color: Optional[str] = "000000", + background_color: Optional[str] = "ffffff", + **kwargs: Any, + ): + super().__init__(**kwargs) + self[NameObject("/Subtype")] = NameObject("/FreeText") + self[NameObject("/Rect")] = RectangleObject(rect) + + font_str = "font: " + if bold: + font_str = f"{font_str}bold " + if italic: + font_str = f"{font_str}italic " + font_str = f"{font_str}{font} {font_size}" + font_str = f"{font_str};text-align:left;color:#{font_color}" + + default_appearance_string = "" + if border_color: + for st in hex_to_rgb(border_color): + default_appearance_string = f"{default_appearance_string}{st} " + default_appearance_string = f"{default_appearance_string}rg" + + self.update( + { + NameObject("/Subtype"): NameObject("/FreeText"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/Contents"): TextStringObject(text), + # font size color + NameObject("/DS"): TextStringObject(font_str), + NameObject("/DA"): TextStringObject(default_appearance_string), + } + ) + if border_color is None: + # Border Style + self[NameObject("/BS")] = DictionaryObject( + { + # width of 0 means no border + NameObject("/W"): NumberObject(0) + } + ) + if background_color is not None: + self[NameObject("/C")] = ArrayObject( + [FloatObject(n) for n in hex_to_rgb(background_color)] + ) + + +class Line(MarkupAnnotation): + def __init__( + self, + p1: Vertex, + p2: Vertex, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + text: str = "", + **kwargs: Any, + ): + super().__init__(**kwargs) + self.update( + { + NameObject("/Subtype"): NameObject("/Line"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/L"): ArrayObject( + [ + FloatObject(p1[0]), + FloatObject(p1[1]), + FloatObject(p2[0]), + FloatObject(p2[1]), + ] + ), + NameObject("/LE"): ArrayObject( + [ + NameObject("/None"), + NameObject("/None"), + ] + ), + NameObject("/IC"): ArrayObject( + [ + FloatObject(0.5), + FloatObject(0.5), + FloatObject(0.5), + ] + ), + NameObject("/Contents"): TextStringObject(text), + } + ) + + +class PolyLine(MarkupAnnotation): + def __init__( + self, + vertices: List[Vertex], + **kwargs: Any, + ): + super().__init__(**kwargs) + if len(vertices) == 0: + raise ValueError("A polygon needs at least 1 vertex with two coordinates") + coord_list = [] + for x, y in vertices: + coord_list.append(NumberObject(x)) + coord_list.append(NumberObject(y)) + self.update( + { + NameObject("/Subtype"): NameObject("/PolyLine"), + NameObject("/Vertices"): ArrayObject(coord_list), + NameObject("/Rect"): RectangleObject(_get_bounding_rectangle(vertices)), + } + ) + + +class Rectangle(MarkupAnnotation): + def __init__( + self, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + *, + interior_color: Optional[str] = None, + **kwargs: Any, + ): + if "interiour_color" in kwargs: + deprecate_with_replacement("interiour_color", "interior_color", "6.0.0") + interior_color = kwargs["interiour_color"] + del kwargs["interiour_color"] + super().__init__(**kwargs) + self.update( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Square"), + NameObject("/Rect"): RectangleObject(rect), + } + ) + + if interior_color: + self[NameObject("/IC")] = ArrayObject( + [FloatObject(n) for n in hex_to_rgb(interior_color)] + ) + + +class Highlight(MarkupAnnotation): + def __init__( + self, + *, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + quad_points: ArrayObject, + highlight_color: str = "ff0000", + printing: bool = False, + **kwargs: Any, + ): + super().__init__(**kwargs) + self.update( + { + NameObject("/Subtype"): NameObject("/Highlight"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/QuadPoints"): quad_points, + NameObject("/C"): ArrayObject( + [FloatObject(n) for n in hex_to_rgb(highlight_color)] + ), + } + ) + if printing: + self.flags = AnnotationFlag.PRINT + + +class Ellipse(MarkupAnnotation): + def __init__( + self, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + *, + interior_color: Optional[str] = None, + **kwargs: Any, + ): + if "interiour_color" in kwargs: + deprecate_with_replacement("interiour_color", "interior_color", "6.0.0") + interior_color = kwargs["interiour_color"] + del kwargs["interiour_color"] + super().__init__(**kwargs) + + self.update( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Circle"), + NameObject("/Rect"): RectangleObject(rect), + } + ) + + if interior_color: + self[NameObject("/IC")] = ArrayObject( + [FloatObject(n) for n in hex_to_rgb(interior_color)] + ) + + +class Polygon(MarkupAnnotation): + def __init__( + self, + vertices: List[Tuple[float, float]], + **kwargs: Any, + ): + super().__init__(**kwargs) + if len(vertices) == 0: + raise ValueError("A polygon needs at least 1 vertex with two coordinates") + + coord_list = [] + for x, y in vertices: + coord_list.append(NumberObject(x)) + coord_list.append(NumberObject(y)) + self.update( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Polygon"), + NameObject("/Vertices"): ArrayObject(coord_list), + NameObject("/IT"): NameObject("/PolygonCloud"), + NameObject("/Rect"): RectangleObject(_get_bounding_rectangle(vertices)), + } + ) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_non_markup_annotations.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_non_markup_annotations.py new file mode 100644 index 00000000..af02223e --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/annotations/_non_markup_annotations.py @@ -0,0 +1,106 @@ +from typing import TYPE_CHECKING, Any, Optional, Tuple, Union + +from ..generic._base import ( + BooleanObject, + NameObject, + NumberObject, + TextStringObject, +) +from ..generic._data_structures import ArrayObject, DictionaryObject +from ..generic._fit import DEFAULT_FIT, Fit +from ..generic._rectangle import RectangleObject +from ._base import AnnotationDictionary + + +class Link(AnnotationDictionary): + def __init__( + self, + *, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + border: Optional[ArrayObject] = None, + url: Optional[str] = None, + target_page_index: Optional[int] = None, + fit: Fit = DEFAULT_FIT, + **kwargs: Any, + ): + super().__init__(**kwargs) + if TYPE_CHECKING: + from ..types import BorderArrayType + + is_external = url is not None + is_internal = target_page_index is not None + if not is_external and not is_internal: + raise ValueError( + "Either 'url' or 'target_page_index' have to be provided. Both were None." + ) + if is_external and is_internal: + raise ValueError( + "Either 'url' or 'target_page_index' have to be provided. " + f"{url=}, {target_page_index=}" + ) + + border_arr: BorderArrayType + if border is not None: + border_arr = [NumberObject(n) for n in border[:3]] + if len(border) == 4: + dash_pattern = ArrayObject([NumberObject(n) for n in border[3]]) + border_arr.append(dash_pattern) + else: + border_arr = [NumberObject(0)] * 3 + + self.update( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Link"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/Border"): ArrayObject(border_arr), + } + ) + if is_external: + self[NameObject("/A")] = DictionaryObject( + { + NameObject("/S"): NameObject("/URI"), + NameObject("/Type"): NameObject("/Action"), + NameObject("/URI"): TextStringObject(url), + } + ) + if is_internal: + # This needs to be updated later! + dest_deferred = DictionaryObject( + { + "target_page_index": NumberObject(target_page_index), + "fit": NameObject(fit.fit_type), + "fit_args": fit.fit_args, + } + ) + self[NameObject("/Dest")] = dest_deferred + + +class Popup(AnnotationDictionary): + def __init__( + self, + *, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + parent: Optional[DictionaryObject] = None, + open: bool = False, + **kwargs: Any, + ): + super().__init__(**kwargs) + self.update( + { + NameObject("/Subtype"): NameObject("/Popup"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/Open"): BooleanObject(open), + } + ) + if parent: + # This needs to be an indirect object + try: + self[NameObject("/Parent")] = parent.indirect_reference + except AttributeError: + from .._utils import logger_warning + + logger_warning( + "Unregistered Parent object : No Parent field set", + __name__, + ) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/constants.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/constants.py new file mode 100644 index 00000000..89fb5535 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/constants.py @@ -0,0 +1,723 @@ +""" +PDF Specification Archive +https://pdfa.org/resource/pdf-specification-archive/ + +Portable Document Format Reference Manual, 1993. ISBN 0-201-62628-4 +https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.0.pdf + +ISO 32000-1:2008 (PDF 1.7) +https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf + +ISO 32000-2:2020 (PDF 2.0) +""" + +from enum import IntFlag, auto +from typing import Dict, Tuple + + +class Core: + """Keywords that don't quite belong anywhere else.""" + + OUTLINES = "/Outlines" + THREADS = "/Threads" + PAGE = "/Page" + PAGES = "/Pages" + CATALOG = "/Catalog" + + +class TrailerKeys: + ROOT = "/Root" + ENCRYPT = "/Encrypt" + ID = "/ID" + INFO = "/Info" + SIZE = "/Size" + PREV = "/Prev" + + +class CatalogAttributes: + NAMES = "/Names" + DESTS = "/Dests" + + +class EncryptionDictAttributes: + """ + Additional encryption dictionary entries for the standard security handler. + + Table 3.19, Page 122. + Table 21 of the 2.0 manual. + """ + + R = "/R" # number, required; revision of the standard security handler + O = "/O" # 32-byte string, required # noqa + U = "/U" # 32-byte string, required + P = "/P" # integer flag, required; permitted operations + ENCRYPT_METADATA = "/EncryptMetadata" # boolean flag, optional + + +class UserAccessPermissions(IntFlag): + """ + Table 3.20 User access permissions. + Table 22 of the 2.0 manual. + """ + + R1 = 1 + R2 = 2 + PRINT = 4 + MODIFY = 8 + EXTRACT = 16 + ADD_OR_MODIFY = 32 + R7 = 64 + R8 = 128 + FILL_FORM_FIELDS = 256 + EXTRACT_TEXT_AND_GRAPHICS = 512 + ASSEMBLE_DOC = 1024 + PRINT_TO_REPRESENTATION = 2048 + R13 = 2**12 + R14 = 2**13 + R15 = 2**14 + R16 = 2**15 + R17 = 2**16 + R18 = 2**17 + R19 = 2**18 + R20 = 2**19 + R21 = 2**20 + R22 = 2**21 + R23 = 2**22 + R24 = 2**23 + R25 = 2**24 + R26 = 2**25 + R27 = 2**26 + R28 = 2**27 + R29 = 2**28 + R30 = 2**29 + R31 = 2**30 + R32 = 2**31 + + @classmethod + def _is_reserved(cls, name: str) -> bool: + """Check if the given name corresponds to a reserved flag entry.""" + return name.startswith("R") and name[1:].isdigit() + + @classmethod + def _is_active(cls, name: str) -> bool: + """Check if the given reserved name defaults to 1 = active.""" + return name not in {"R1", "R2"} + + def to_dict(self) -> Dict[str, bool]: + """Convert the given flag value to a corresponding verbose name mapping.""" + result: Dict[str, bool] = {} + for name, flag in UserAccessPermissions.__members__.items(): + if UserAccessPermissions._is_reserved(name): + continue + result[name.lower()] = (self & flag) == flag + return result + + @classmethod + def from_dict(cls, value: Dict[str, bool]) -> "UserAccessPermissions": + """Convert the verbose name mapping to the corresponding flag value.""" + value_copy = value.copy() + result = cls(0) + for name, flag in cls.__members__.items(): + if cls._is_reserved(name): + # Reserved names have a required value. Use it. + if cls._is_active(name): + result |= flag + continue + is_active = value_copy.pop(name.lower(), False) + if is_active: + result |= flag + if value_copy: + raise ValueError(f"Unknown dictionary keys: {value_copy!r}") + return result + + @classmethod + def all(cls) -> "UserAccessPermissions": + return cls((2**32 - 1) - cls.R1 - cls.R2) + + +class Resources: + """ + Table 3.30 Entries in a resource dictionary. + Used to be Ressources (a misspelling). + + Table 34 in the 2.0 reference. + """ + + EXT_G_STATE = "/ExtGState" # dictionary, optional + COLOR_SPACE = "/ColorSpace" # dictionary, optional + PATTERN = "/Pattern" # dictionary, optional + SHADING = "/Shading" # dictionary, optional + XOBJECT = "/XObject" # dictionary, optional + FONT = "/Font" # dictionary, optional + PROC_SET = "/ProcSet" # array, optional + PROPERTIES = "/Properties" # dictionary, optional + + +class Ressources: # deprecated + """ + Use :class: `Resources` instead. + + .. deprecated:: 5.0.0 + """ + + +class PagesAttributes: + """§7.7.3.2 of the 1.7 and 2.0 reference.""" + + TYPE = "/Type" # name, required; must be /Pages + PARENT = "/Parent" # dictionary, required; indirect reference to pages object + KIDS = "/Kids" # array, required; List of indirect references + COUNT = "/Count" # integer, required; the number of leaf nodes (page objects) + # that are descendants of this node within the page tree + + +class PageAttributes: + """§7.7.3.3 of the 1.7 and 2.0 reference.""" + + TYPE = "/Type" # name, required; must be /Page + PARENT = "/Parent" # dictionary, required; a pages object + LAST_MODIFIED = ( + "/LastModified" # date, optional; date and time of last modification + ) + RESOURCES = "/Resources" # dictionary, required if there are any + MEDIABOX = "/MediaBox" # rectangle, required; rectangle specifying page size + CROPBOX = "/CropBox" # rectangle, optional + BLEEDBOX = "/BleedBox" # rectangle, optional + TRIMBOX = "/TrimBox" # rectangle, optional + ARTBOX = "/ArtBox" # rectangle, optional + BOX_COLOR_INFO = "/BoxColorInfo" # dictionary, optional + CONTENTS = "/Contents" # stream or array, optional + ROTATE = "/Rotate" # integer, optional; page rotation in degrees + GROUP = "/Group" # dictionary, optional; page group + THUMB = "/Thumb" # stream, optional; indirect reference to image of the page + B = "/B" # array, optional + DUR = "/Dur" # number, optional + TRANS = "/Trans" # dictionary, optional + ANNOTS = "/Annots" # array, optional; an array of annotations + AA = "/AA" # dictionary, optional + METADATA = "/Metadata" # stream, optional + PIECE_INFO = "/PieceInfo" # dictionary, optional + STRUCT_PARENTS = "/StructParents" # integer, optional + ID = "/ID" # byte string, optional + PZ = "/PZ" # number, optional + SEPARATION_INFO = "/SeparationInfo" # dictionary, optional + TABS = "/Tabs" # name, optional + TEMPLATE_INSTANTIATED = "/TemplateInstantiated" # name, optional + PRES_STEPS = "/PresSteps" # dictionary, optional + USER_UNIT = "/UserUnit" # number, optional + VP = "/VP" # dictionary, optional + AF = "/AF" # array of dictionaries, optional + OUTPUT_INTENTS = "/OutputIntents" # array, optional + D_PART = "/DPart" # dictionary, required, if this page is within the range of a DPart, not permitted otherwise + + +class FileSpecificationDictionaryEntries: + """Table 3.41 Entries in a file specification dictionary.""" + + Type = "/Type" + FS = "/FS" # The name of the file system to be used to interpret this file specification + F = "/F" # A file specification string of the form described in Section 3.10.1 + UF = "/UF" # A unicode string of the file as described in Section 3.10.1 + DOS = "/DOS" + Mac = "/Mac" + Unix = "/Unix" + ID = "/ID" + V = "/V" + EF = "/EF" # dictionary, containing a subset of the keys F , UF , DOS , Mac , and Unix + RF = "/RF" # dictionary, containing arrays of /EmbeddedFile + DESC = "/Desc" # description of the file + Cl = "/Cl" + + +class StreamAttributes: + """ + Table 4.2. + Table 5 in the 2.0 reference. + """ + + LENGTH = "/Length" # integer, required + FILTER = "/Filter" # name or array of names, optional + DECODE_PARMS = "/DecodeParms" # variable, optional -- 'decodeParams is wrong + + +class FilterTypes: + """§7.4 of the 1.7 and 2.0 references.""" + + ASCII_HEX_DECODE = "/ASCIIHexDecode" # abbreviation: AHx + ASCII_85_DECODE = "/ASCII85Decode" # abbreviation: A85 + LZW_DECODE = "/LZWDecode" # abbreviation: LZW + FLATE_DECODE = "/FlateDecode" # abbreviation: Fl, PDF 1.2 + RUN_LENGTH_DECODE = "/RunLengthDecode" # abbreviation: RL + CCITT_FAX_DECODE = "/CCITTFaxDecode" # abbreviation: CCF + DCT_DECODE = "/DCTDecode" # abbreviation: DCT + JPX_DECODE = "/JPXDecode" + + +class FilterTypeAbbreviations: + """§8.9.7 of the 1.7 and 2.0 references.""" + + AHx = "/AHx" + A85 = "/A85" + LZW = "/LZW" + FL = "/Fl" # FlateDecode + RL = "/RL" + CCF = "/CCF" + DCT = "/DCT" + + +class LzwFilterParameters: + """ + Table 4.4. + Table 8 in the 2.0 reference. + """ + + PREDICTOR = "/Predictor" # integer + COLORS = "/Colors" # integer + BITS_PER_COMPONENT = "/BitsPerComponent" # integer + COLUMNS = "/Columns" # integer + EARLY_CHANGE = "/EarlyChange" # integer + + +class CcittFaxDecodeParameters: + """ + Table 4.5. + Table 11 in the 2.0 reference. + """ + + K = "/K" # integer + END_OF_LINE = "/EndOfLine" # boolean + ENCODED_BYTE_ALIGN = "/EncodedByteAlign" # boolean + COLUMNS = "/Columns" # integer + ROWS = "/Rows" # integer + END_OF_BLOCK = "/EndOfBlock" # boolean + BLACK_IS_1 = "/BlackIs1" # boolean + DAMAGED_ROWS_BEFORE_ERROR = "/DamagedRowsBeforeError" # integer + + +class ImageAttributes: + """§11.6.5 of the 1.7 and 2.0 references.""" + + TYPE = "/Type" # name, required; must be /XObject + SUBTYPE = "/Subtype" # name, required; must be /Image + NAME = "/Name" # name, required + WIDTH = "/Width" # integer, required + HEIGHT = "/Height" # integer, required + BITS_PER_COMPONENT = "/BitsPerComponent" # integer, required + COLOR_SPACE = "/ColorSpace" # name, required + DECODE = "/Decode" # array, optional + INTENT = "/Intent" # string, optional + INTERPOLATE = "/Interpolate" # boolean, optional + IMAGE_MASK = "/ImageMask" # boolean, optional + MASK = "/Mask" # 1-bit image mask stream + S_MASK = "/SMask" # dictionary or name, optional + + +class ColorSpaces: + DEVICE_RGB = "/DeviceRGB" + DEVICE_CMYK = "/DeviceCMYK" + DEVICE_GRAY = "/DeviceGray" + + +class TypArguments: + """Table 8.2 of the PDF 1.7 reference.""" + + LEFT = "/Left" + RIGHT = "/Right" + BOTTOM = "/Bottom" + TOP = "/Top" + + +class TypFitArguments: + """Table 8.2 of the PDF 1.7 reference.""" + + FIT = "/Fit" + FIT_V = "/FitV" + FIT_BV = "/FitBV" + FIT_B = "/FitB" + FIT_H = "/FitH" + FIT_BH = "/FitBH" + FIT_R = "/FitR" + XYZ = "/XYZ" + + +class GoToActionArguments: + S = "/S" # name, required: type of action + D = "/D" # name / byte string /array, required: Destination to jump to + + +class AnnotationDictionaryAttributes: + """Table 8.15 Entries common to all annotation dictionaries.""" + + Type = "/Type" + Subtype = "/Subtype" + Rect = "/Rect" + Contents = "/Contents" + P = "/P" + NM = "/NM" + M = "/M" + F = "/F" + AP = "/AP" + AS = "/AS" + DA = "/DA" + Border = "/Border" + C = "/C" + StructParent = "/StructParent" + OC = "/OC" + + +class InteractiveFormDictEntries: + Fields = "/Fields" + NeedAppearances = "/NeedAppearances" + SigFlags = "/SigFlags" + CO = "/CO" + DR = "/DR" + DA = "/DA" + Q = "/Q" + XFA = "/XFA" + + +class FieldDictionaryAttributes: + """ + Entries common to all field dictionaries (Table 8.69 PDF 1.7 reference) + (*very partially documented here*). + + FFBits provides the constants used for `/Ff` from Table 8.70/8.75/8.77/8.79 + """ + + FT = "/FT" # name, required for terminal fields + Parent = "/Parent" # dictionary, required for children + Kids = "/Kids" # array, sometimes required + T = "/T" # text string, optional + TU = "/TU" # text string, optional + TM = "/TM" # text string, optional + Ff = "/Ff" # integer, optional + V = "/V" # text string or array, optional + DV = "/DV" # text string, optional + AA = "/AA" # dictionary, optional + Opt = "/Opt" + + class FfBits(IntFlag): + """ + Ease building /Ff flags + Some entries may be specific to: + + * Text(Tx) (Table 8.75 PDF 1.7 reference) + * Buttons(Btn) (Table 8.77 PDF 1.7 reference) + * List(Ch) (Table 8.79 PDF 1.7 reference) + """ + + ReadOnly = 1 << 0 + """common to Tx/Btn/Ch in Table 8.70""" + Required = 1 << 1 + """common to Tx/Btn/Ch in Table 8.70""" + NoExport = 1 << 2 + """common to Tx/Btn/Ch in Table 8.70""" + + Multiline = 1 << 12 + """Tx""" + Password = 1 << 13 + """Tx""" + + NoToggleToOff = 1 << 14 + """Btn""" + Radio = 1 << 15 + """Btn""" + Pushbutton = 1 << 16 + """Btn""" + + Combo = 1 << 17 + """Ch""" + Edit = 1 << 18 + """Ch""" + Sort = 1 << 19 + """Ch""" + + FileSelect = 1 << 20 + """Tx""" + + MultiSelect = 1 << 21 + """Tx""" + + DoNotSpellCheck = 1 << 22 + """Tx/Ch""" + DoNotScroll = 1 << 23 + """Tx""" + Comb = 1 << 24 + """Tx""" + + RadiosInUnison = 1 << 25 + """Btn""" + + RichText = 1 << 25 + """Tx""" + + CommitOnSelChange = 1 << 26 + """Ch""" + + @classmethod + def attributes(cls) -> Tuple[str, ...]: + """ + Get a tuple of all the attributes present in a Field Dictionary. + + This method returns a tuple of all the attribute constants defined in + the FieldDictionaryAttributes class. These attributes correspond to the + entries that are common to all field dictionaries as specified in the + PDF 1.7 reference. + + Returns: + A tuple containing all the attribute constants. + """ + return ( + cls.TM, + cls.T, + cls.FT, + cls.Parent, + cls.TU, + cls.Ff, + cls.V, + cls.DV, + cls.Kids, + cls.AA, + ) + + @classmethod + def attributes_dict(cls) -> Dict[str, str]: + """ + Get a dictionary of attribute keys and their human-readable names. + + This method returns a dictionary where the keys are the attribute + constants defined in the FieldDictionaryAttributes class and the values + are their corresponding human-readable names. These attributes + correspond to the entries that are common to all field dictionaries as + specified in the PDF 1.7 reference. + + Returns: + A dictionary containing attribute keys and their names. + """ + return { + cls.FT: "Field Type", + cls.Parent: "Parent", + cls.T: "Field Name", + cls.TU: "Alternate Field Name", + cls.TM: "Mapping Name", + cls.Ff: "Field Flags", + cls.V: "Value", + cls.DV: "Default Value", + } + + +class CheckboxRadioButtonAttributes: + """Table 8.76 Field flags common to all field types.""" + + Opt = "/Opt" # Options, Optional + + @classmethod + def attributes(cls) -> Tuple[str, ...]: + """ + Get a tuple of all the attributes present in a Field Dictionary. + + This method returns a tuple of all the attribute constants defined in + the CheckboxRadioButtonAttributes class. These attributes correspond to + the entries that are common to all field dictionaries as specified in + the PDF 1.7 reference. + + Returns: + A tuple containing all the attribute constants. + """ + return (cls.Opt,) + + @classmethod + def attributes_dict(cls) -> Dict[str, str]: + """ + Get a dictionary of attribute keys and their human-readable names. + + This method returns a dictionary where the keys are the attribute + constants defined in the CheckboxRadioButtonAttributes class and the + values are their corresponding human-readable names. These attributes + correspond to the entries that are common to all field dictionaries as + specified in the PDF 1.7 reference. + + Returns: + A dictionary containing attribute keys and their names. + """ + return { + cls.Opt: "Options", + } + + +class FieldFlag(IntFlag): + """Table 8.70 Field flags common to all field types.""" + + READ_ONLY = 1 + REQUIRED = 2 + NO_EXPORT = 4 + + +class DocumentInformationAttributes: + """Table 10.2 Entries in the document information dictionary.""" + + TITLE = "/Title" # text string, optional + AUTHOR = "/Author" # text string, optional + SUBJECT = "/Subject" # text string, optional + KEYWORDS = "/Keywords" # text string, optional + CREATOR = "/Creator" # text string, optional + PRODUCER = "/Producer" # text string, optional + CREATION_DATE = "/CreationDate" # date, optional + MOD_DATE = "/ModDate" # date, optional + TRAPPED = "/Trapped" # name, optional + + +class PageLayouts: + """ + Page 84, PDF 1.4 reference. + Page 115, PDF 2.0 reference. + """ + + SINGLE_PAGE = "/SinglePage" + ONE_COLUMN = "/OneColumn" + TWO_COLUMN_LEFT = "/TwoColumnLeft" + TWO_COLUMN_RIGHT = "/TwoColumnRight" + TWO_PAGE_LEFT = "/TwoPageLeft" # (PDF 1.5) + TWO_PAGE_RIGHT = "/TwoPageRight" # (PDF 1.5) + + +class GraphicsStateParameters: + """Table 58 – Entries in a Graphics State Parameter Dictionary""" + + TYPE = "/Type" # name, optional + LW = "/LW" # number, optional + LC = "/LC" # integer, optional + LJ = "/LJ" # integer, optional + ML = "/ML" # number, optional + D = "/D" # array, optional + RI = "/RI" # name, optional + OP = "/OP" + op = "/op" + OPM = "/OPM" + FONT = "/Font" # array, optional + BG = "/BG" + BG2 = "/BG2" + UCR = "/UCR" + UCR2 = "/UCR2" + TR = "/TR" + TR2 = "/TR2" + HT = "/HT" + FL = "/FL" + SM = "/SM" + SA = "/SA" + BM = "/BM" + S_MASK = "/SMask" # dictionary or name, optional + CA = "/CA" + ca = "/ca" + AIS = "/AIS" + TK = "/TK" + + +class CatalogDictionary: + """§7.7.2 of the 1.7 and 2.0 references.""" + + TYPE = "/Type" # name, required; must be /Catalog + VERSION = "/Version" # name + EXTENSIONS = "/Extensions" # dictionary, optional; ISO 32000-1 + PAGES = "/Pages" # dictionary, required + PAGE_LABELS = "/PageLabels" # number tree, optional + NAMES = "/Names" # dictionary, optional + DESTS = "/Dests" # dictionary, optional + VIEWER_PREFERENCES = "/ViewerPreferences" # dictionary, optional + PAGE_LAYOUT = "/PageLayout" # name, optional + PAGE_MODE = "/PageMode" # name, optional + OUTLINES = "/Outlines" # dictionary, optional + THREADS = "/Threads" # array, optional + OPEN_ACTION = "/OpenAction" # array or dictionary or name, optional + AA = "/AA" # dictionary, optional + URI = "/URI" # dictionary, optional + ACRO_FORM = "/AcroForm" # dictionary, optional + METADATA = "/Metadata" # stream, optional + STRUCT_TREE_ROOT = "/StructTreeRoot" # dictionary, optional + MARK_INFO = "/MarkInfo" # dictionary, optional + LANG = "/Lang" # text string, optional + SPIDER_INFO = "/SpiderInfo" # dictionary, optional + OUTPUT_INTENTS = "/OutputIntents" # array, optional + PIECE_INFO = "/PieceInfo" # dictionary, optional + OC_PROPERTIES = "/OCProperties" # dictionary, optional + PERMS = "/Perms" # dictionary, optional + LEGAL = "/Legal" # dictionary, optional + REQUIREMENTS = "/Requirements" # array, optional + COLLECTION = "/Collection" # dictionary, optional + NEEDS_RENDERING = "/NeedsRendering" # boolean, optional + DSS = "/DSS" # dictionary, optional + AF = "/AF" # array of dictionaries, optional + D_PART_ROOT = "/DPartRoot" # dictionary, optional + + +class OutlineFontFlag(IntFlag): + """A class used as an enumerable flag for formatting an outline font.""" + + italic = 1 + bold = 2 + + +class PageLabelStyle: + """ + Table 8.10 in the 1.7 reference. + Table 161 in the 2.0 reference. + """ + + DECIMAL = "/D" # Decimal Arabic numerals + UPPERCASE_ROMAN = "/R" # Uppercase Roman numerals + LOWERCASE_ROMAN = "/r" # Lowercase Roman numerals + UPPERCASE_LETTER = "/A" # Uppercase letters + LOWERCASE_LETTER = "/a" # Lowercase letters + + +class AnnotationFlag(IntFlag): + """See §12.5.3 "Annotation Flags".""" + + INVISIBLE = 1 + HIDDEN = 2 + PRINT = 4 + NO_ZOOM = 8 + NO_ROTATE = 16 + NO_VIEW = 32 + READ_ONLY = 64 + LOCKED = 128 + TOGGLE_NO_VIEW = 256 + LOCKED_CONTENTS = 512 + + +PDF_KEYS = ( + AnnotationDictionaryAttributes, + CatalogAttributes, + CatalogDictionary, + CcittFaxDecodeParameters, + CheckboxRadioButtonAttributes, + ColorSpaces, + Core, + DocumentInformationAttributes, + EncryptionDictAttributes, + FieldDictionaryAttributes, + FilterTypeAbbreviations, + FilterTypes, + GoToActionArguments, + GraphicsStateParameters, + ImageAttributes, + FileSpecificationDictionaryEntries, + LzwFilterParameters, + PageAttributes, + PageLayouts, + PagesAttributes, + Resources, + StreamAttributes, + TrailerKeys, + TypArguments, + TypFitArguments, +) + + +class ImageType(IntFlag): + NONE = 0 + XOBJECT_IMAGES = auto() + INLINE_IMAGES = auto() + DRAWING_IMAGES = auto() + ALL = XOBJECT_IMAGES | INLINE_IMAGES | DRAWING_IMAGES + IMAGES = ALL # for consistency with ObjectDeletionFlag diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/errors.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/errors.py new file mode 100644 index 00000000..ad197ffc --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/errors.py @@ -0,0 +1,66 @@ +""" +All errors/exceptions pypdf raises and all of the warnings it uses. + +Please note that broken PDF files might cause other Exceptions. +""" + + +class DeprecationError(Exception): + """Raised when a deprecated feature is used.""" + + +class DependencyError(Exception): + """ + Raised when a required dependency (a library or module that PyPDF depends on) + is not available or cannot be imported. + """ + + +class PyPdfError(Exception): + """Base class for all exceptions raised by PyPDF.""" + + +class PdfReadError(PyPdfError): + """Raised when there is an issue reading a PDF file.""" + + +class PageSizeNotDefinedError(PyPdfError): + """Raised when the page size of a PDF document is not defined.""" + + +class PdfReadWarning(UserWarning): + """Issued when there is a potential issue reading a PDF file, but it can still be read.""" + + +class PdfStreamError(PdfReadError): + """Raised when there is an issue reading the stream of data in a PDF file.""" + + +class ParseError(PyPdfError): + """ + Raised when there is an issue parsing (analyzing and understanding the + structure and meaning of) a PDF file. + """ + + +class FileNotDecryptedError(PdfReadError): + """ + Raised when a PDF file that has been encrypted + (meaning it requires a password to be accessed) has not been successfully + decrypted. + """ + + +class WrongPasswordError(FileNotDecryptedError): + """Raised when the wrong password is used to try to decrypt an encrypted PDF file.""" + + +class EmptyFileError(PdfReadError): + """Raised when a PDF file is empty or has no content.""" + + +class EmptyImageDataError(PyPdfError): + """Raised when trying to process an image that has no data.""" + + +STREAM_TRUNCATED_PREMATURELY = "Stream has ended unexpectedly" diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/filters.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/filters.py new file mode 100644 index 00000000..e2fdd0d8 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/filters.py @@ -0,0 +1,912 @@ +# Copyright (c) 2006, Mathieu Fenniak +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +""" +Implementation of stream filters for PDF. + +See TABLE H.1 Abbreviations for standard filter names +""" +__author__ = "Mathieu Fenniak" +__author_email__ = "biziqe@mathieu.fenniak.net" + +import math +import struct +import zlib +from base64 import a85decode +from io import BytesIO +from typing import Any, Dict, List, Optional, Tuple, Union, cast + +from ._utils import ( + WHITESPACES_AS_BYTES, + deprecate, + deprecation_no_replacement, + logger_warning, + ord_, +) +from .constants import CcittFaxDecodeParameters as CCITT +from .constants import ColorSpaces +from .constants import FilterTypeAbbreviations as FTA +from .constants import FilterTypes as FT +from .constants import ImageAttributes as IA +from .constants import LzwFilterParameters as LZW +from .constants import StreamAttributes as SA +from .errors import DeprecationError, PdfReadError, PdfStreamError +from .generic import ( + ArrayObject, + DictionaryObject, + IndirectObject, + NullObject, +) + + +def decompress(data: bytes) -> bytes: + """ + Decompress the given data using zlib. + + This function attempts to decompress the input data using zlib. If the + decompression fails due to a zlib error, it falls back to using a + decompression object with a larger window size. + + Args: + data: The input data to be decompressed. + + Returns: + The decompressed data. + """ + try: + return zlib.decompress(data) + except zlib.error: + try: + # For larger files, use Decompress object to enable buffered reading + return zlib.decompressobj().decompress(data) + except zlib.error: + # If still failed, then try with increased window size + d = zlib.decompressobj(zlib.MAX_WBITS | 32) + result_str = b"" + for b in [data[i : i + 1] for i in range(len(data))]: + try: + result_str += d.decompress(b) + except zlib.error: + pass + return result_str + + +class FlateDecode: + @staticmethod + def decode( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + """ + Decode data which is flate-encoded. + + Args: + data: flate-encoded data. + decode_parms: a dictionary of values, understanding the + "/Predictor": key only + + Returns: + The flate-decoded data. + + Raises: + PdfReadError: + """ + if isinstance(decode_parms, ArrayObject): + raise DeprecationError("decode_parms as ArrayObject is depreciated") + + str_data = decompress(data) + predictor = 1 + + if decode_parms: + try: + predictor = decode_parms.get("/Predictor", 1) + except (AttributeError, TypeError): # Type Error is NullObject + pass # Usually an array with a null object was read + # predictor 1 == no predictor + if predictor != 1: + # /Columns, the number of samples in each row, has a default value of 1; + # §7.4.4.3, ISO 32000. + DEFAULT_BITS_PER_COMPONENT = 8 + try: + columns = cast(int, decode_parms[LZW.COLUMNS].get_object()) # type: ignore + except (TypeError, KeyError): + columns = 1 + try: + colors = cast(int, decode_parms[LZW.COLORS].get_object()) # type: ignore + except (TypeError, KeyError): + colors = 1 + try: + bits_per_component = cast( + int, + decode_parms[LZW.BITS_PER_COMPONENT].get_object(), # type: ignore + ) + except (TypeError, KeyError): + bits_per_component = DEFAULT_BITS_PER_COMPONENT + + # PNG predictor can vary by row and so is the lead byte on each row + rowlength = ( + math.ceil(columns * colors * bits_per_component / 8) + 1 + ) # number of bytes + + # TIFF prediction: + if predictor == 2: + rowlength -= 1 # remove the predictor byte + bpp = rowlength // columns + str_data = bytearray(str_data) + for i in range(len(str_data)): + if i % rowlength >= bpp: + str_data[i] = (str_data[i] + str_data[i - bpp]) % 256 + str_data = bytes(str_data) + # PNG prediction: + elif 10 <= predictor <= 15: + str_data = FlateDecode._decode_png_prediction( + str_data, columns, rowlength + ) + else: + # unsupported predictor + raise PdfReadError(f"Unsupported flatedecode predictor {predictor!r}") + return str_data + + @staticmethod + def _decode_png_prediction(data: bytes, columns: int, rowlength: int) -> bytes: + # PNG prediction can vary from row to row + if len(data) % rowlength != 0: + raise PdfReadError("Image data is not rectangular") + output = [] + prev_rowdata = (0,) * rowlength + bpp = (rowlength - 1) // columns # recomputed locally to not change params + for row in range(0, len(data), rowlength): + rowdata: List[int] = list(data[row : row + rowlength]) + filter_byte = rowdata[0] + + if filter_byte == 0: + # PNG None Predictor + pass + elif filter_byte == 1: + # PNG Sub Predictor + for i in range(bpp + 1, rowlength): + rowdata[i] = (rowdata[i] + rowdata[i - bpp]) % 256 + elif filter_byte == 2: + # PNG Up Predictor + for i in range(1, rowlength): + rowdata[i] = (rowdata[i] + prev_rowdata[i]) % 256 + elif filter_byte == 3: + # PNG Average Predictor + for i in range(1, bpp + 1): + floor = prev_rowdata[i] // 2 + rowdata[i] = (rowdata[i] + floor) % 256 + for i in range(bpp + 1, rowlength): + left = rowdata[i - bpp] + floor = (left + prev_rowdata[i]) // 2 + rowdata[i] = (rowdata[i] + floor) % 256 + elif filter_byte == 4: + # PNG Paeth Predictor + for i in range(1, bpp + 1): + rowdata[i] = (rowdata[i] + prev_rowdata[i]) % 256 + for i in range(bpp + 1, rowlength): + left = rowdata[i - bpp] + up = prev_rowdata[i] + up_left = prev_rowdata[i - bpp] + + p = left + up - up_left + dist_left = abs(p - left) + dist_up = abs(p - up) + dist_up_left = abs(p - up_left) + + if dist_left <= dist_up and dist_left <= dist_up_left: + paeth = left + elif dist_up <= dist_up_left: + paeth = up + else: + paeth = up_left + + rowdata[i] = (rowdata[i] + paeth) % 256 + else: + # unsupported PNG filter + raise PdfReadError( + f"Unsupported PNG filter {filter_byte!r}" + ) # pragma: no cover + prev_rowdata = tuple(rowdata) + output.extend(rowdata[1:]) + return bytes(output) + + @staticmethod + def encode(data: bytes, level: int = -1) -> bytes: + """ + Compress the input data using zlib. + + Args: + data: The data to be compressed. + level: See https://docs.python.org/3/library/zlib.html#zlib.compress + + Returns: + The compressed data. + """ + return zlib.compress(data, level) + + +class ASCIIHexDecode: + """ + The ASCIIHexDecode filter decodes data that has been encoded in ASCII + hexadecimal form into a base-7 ASCII format. + """ + + @staticmethod + def decode( + data: Union[str, bytes], + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + """ + Decode an ASCII-Hex encoded data stream. + + Args: + data: a str sequence of hexadecimal-encoded values to be + converted into a base-7 ASCII string + decode_parms: a string conversion in base-7 ASCII, where each of its values + v is such that 0 <= ord(v) <= 127. + + Returns: + A string conversion in base-7 ASCII, where each of its values + v is such that 0 <= ord(v) <= 127. + + Raises: + PdfStreamError: + """ + # decode_parms is unused here + + if isinstance(data, str): + data = data.encode() + retval = b"" + hex_pair = b"" + index = 0 + while True: + if index >= len(data): + logger_warning( + "missing EOD in ASCIIHexDecode, check if output is OK", __name__ + ) + break # reach End Of String even if no EOD + char = data[index : index + 1] + if char == b">": + break + elif char.isspace(): + index += 1 + continue + hex_pair += char + if len(hex_pair) == 2: + retval += bytes((int(hex_pair, base=16),)) + hex_pair = b"" + index += 1 + assert hex_pair == b"" + return retval + + +class RunLengthDecode: + """ + The RunLengthDecode filter decodes data that has been encoded in a + simple byte-oriented format based on run length. + The encoded data is a sequence of runs, where each run consists of + a length byte followed by 1 to 128 bytes of data. If the length byte is + in the range 0 to 127, + the following length + 1 (1 to 128) bytes are copied literally during + decompression. + If length is in the range 129 to 255, the following single byte is to be + copied 257 − length (2 to 128) times during decompression. A length value + of 128 denotes EOD. + """ + + @staticmethod + def decode( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + """ + Decode a run length encoded data stream. + + Args: + data: a bytes sequence of length/data + decode_parms: ignored. + + Returns: + A bytes decompressed sequence. + + Raises: + PdfStreamError: + """ + # decode_parms is unused here + + lst = [] + index = 0 + while True: + if index >= len(data): + logger_warning( + "missing EOD in RunLengthDecode, check if output is OK", __name__ + ) + break # reach End Of String even if no EOD + length = data[index] + index += 1 + if length == 128: + if index < len(data): + raise PdfStreamError("early EOD in RunLengthDecode") + else: + break + elif length < 128: + length += 1 + lst.append(data[index : (index + length)]) + index += length + else: # >128 + length = 257 - length + lst.append(bytes((data[index],)) * length) + index += 1 + return b"".join(lst) + + +class LZWDecode: + """ + Taken from: + + http://www.java2s.com/Open-Source/Java-Document/PDF/PDF-Renderer/com/sun/pdfview/decode/LZWDecode.java.htm + """ + + class Decoder: + STOP = 257 + CLEARDICT = 256 + + def __init__(self, data: bytes) -> None: + self.data = data + self.bytepos = 0 + self.bitpos = 0 + self.dict = [struct.pack("B", i) for i in range(256)] + [b""] * (4096 - 256) + self.reset_dict() + + def reset_dict(self) -> None: + self.dictlen = 258 + self.bitspercode = 9 + + def next_code(self) -> int: + fillbits = self.bitspercode + value = 0 + while fillbits > 0: + if self.bytepos >= len(self.data): + return -1 + nextbits = ord_(self.data[self.bytepos]) + bitsfromhere = 8 - self.bitpos + bitsfromhere = min(bitsfromhere, fillbits) + value |= ( + (nextbits >> (8 - self.bitpos - bitsfromhere)) + & (0xFF >> (8 - bitsfromhere)) + ) << (fillbits - bitsfromhere) + fillbits -= bitsfromhere + self.bitpos += bitsfromhere + if self.bitpos >= 8: + self.bitpos = 0 + self.bytepos = self.bytepos + 1 + return value + + def decode(self) -> bytes: + """ + TIFF 6.0 specification explains in sufficient details the steps to + implement the LZW encode() and decode() algorithms. + + algorithm derived from: + http://www.rasip.fer.hr/research/compress/algorithms/fund/lz/lzw.html + and the PDFReference + + Raises: + PdfReadError: If the stop code is missing + """ + cW = self.CLEARDICT + baos = b"" + while True: + pW = cW + cW = self.next_code() + if cW == -1: + raise PdfReadError("Missed the stop code in LZWDecode!") + if cW == self.STOP: + break + elif cW == self.CLEARDICT: + self.reset_dict() + elif pW == self.CLEARDICT: + baos += self.dict[cW] + else: + if cW < self.dictlen: + baos += self.dict[cW] + p = self.dict[pW] + self.dict[cW][0:1] + self.dict[self.dictlen] = p + self.dictlen += 1 + else: + p = self.dict[pW] + self.dict[pW][0:1] + baos += p + self.dict[self.dictlen] = p + self.dictlen += 1 + if ( + self.dictlen >= (1 << self.bitspercode) - 1 + and self.bitspercode < 12 + ): + self.bitspercode += 1 + return baos + + @staticmethod + def _decodeb( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + """ + Decode an LZW encoded data stream. + + Args: + data: ``bytes`` or ``str`` text to decode. + decode_parms: a dictionary of parameter values. + + Returns: + decoded data. + """ + # decode_parms is unused here + return LZWDecode.Decoder(data).decode() + + @staticmethod + def decode( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> str: # deprecated + """ + Decode an LZW encoded data stream. + + Args: + data: ``bytes`` or ``str`` text to decode. + decode_parms: a dictionary of parameter values. + + Returns: + decoded data. + """ + # decode_parms is unused here + deprecate("LZWDecode.decode will return bytes instead of str in pypdf 6.0.0") + return LZWDecode.Decoder(data).decode().decode("latin-1") + + +class ASCII85Decode: + """Decodes string ASCII85-encoded data into a byte format.""" + + @staticmethod + def decode( + data: Union[str, bytes], + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + """ + Decode an Ascii85 encoded data stream. + + Args: + data: ``bytes`` or ``str`` text to decode. + decode_parms: a dictionary of parameter values. + + Returns: + decoded data. + """ + if isinstance(data, str): + data = data.encode() + data = data.strip(WHITESPACES_AS_BYTES) + return a85decode(data, adobe=True, ignorechars=WHITESPACES_AS_BYTES) + + +class DCTDecode: + @staticmethod + def decode( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + # decode_parms is unused here + return data + + +class JPXDecode: + @staticmethod + def decode( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + **kwargs: Any, + ) -> bytes: + # decode_parms is unused here + return data + + +class CCITParameters: + """§7.4.6, optional parameters for the CCITTFaxDecode filter.""" + + def __init__(self, K: int = 0, columns: int = 0, rows: int = 0) -> None: + self.K = K + self.EndOfBlock = None + self.EndOfLine = None + self.EncodedByteAlign = None + self.columns = columns # width + self.rows = rows # height + self.DamagedRowsBeforeError = None + + @property + def group(self) -> int: + if self.K < 0: + CCITTgroup = 4 + else: + # k == 0: Pure one-dimensional encoding (Group 3, 1-D) + # k > 0: Mixed one- and two-dimensional encoding (Group 3, 2-D) + CCITTgroup = 3 + return CCITTgroup + + +class CCITTFaxDecode: + """ + §7.4.6, CCITTFaxDecode filter (ISO 32000). + + Either Group 3 or Group 4 CCITT facsimile (fax) encoding. + CCITT encoding is bit-oriented, not byte-oriented. + + §7.4.6, optional parameters for the CCITTFaxDecode filter. + """ + + @staticmethod + def _get_parameters( + parameters: Union[None, ArrayObject, DictionaryObject, IndirectObject], + rows: int, + ) -> CCITParameters: + # §7.4.6, optional parameters for the CCITTFaxDecode filter + k = 0 + columns = 1728 + if parameters: + parameters_unwrapped = cast( + Union[ArrayObject, DictionaryObject], parameters.get_object() + ) + if isinstance(parameters_unwrapped, ArrayObject): + for decode_parm in parameters_unwrapped: + if CCITT.COLUMNS in decode_parm: + columns = decode_parm[CCITT.COLUMNS] + if CCITT.K in decode_parm: + k = decode_parm[CCITT.K] + else: + if CCITT.COLUMNS in parameters_unwrapped: + columns = parameters_unwrapped[CCITT.COLUMNS] # type: ignore + if CCITT.K in parameters_unwrapped: + k = parameters_unwrapped[CCITT.K] # type: ignore + + return CCITParameters(k, columns, rows) + + @staticmethod + def decode( + data: bytes, + decode_parms: Optional[DictionaryObject] = None, + height: int = 0, + **kwargs: Any, + ) -> bytes: + # decode_parms is unused here + if isinstance(decode_parms, ArrayObject): # deprecated + deprecation_no_replacement( + "decode_parms being an ArrayObject", removed_in="3.15.5" + ) + params = CCITTFaxDecode._get_parameters(decode_parms, height) + + img_size = len(data) + tiff_header_struct = "<2shlh" + "hhll" * 8 + "h" + tiff_header = struct.pack( + tiff_header_struct, + b"II", # Byte order indication: Little endian + 42, # Version number (always 42) + 8, # Offset to first IFD + 8, # Number of tags in IFD + 256, + 4, + 1, + params.columns, # ImageWidth, LONG, 1, width + 257, + 4, + 1, + params.rows, # ImageLength, LONG, 1, length + 258, + 3, + 1, + 1, # BitsPerSample, SHORT, 1, 1 + 259, + 3, + 1, + params.group, # Compression, SHORT, 1, 4 = CCITT Group 4 fax encoding + 262, + 3, + 1, + 0, # Thresholding, SHORT, 1, 0 = WhiteIsZero + 273, + 4, + 1, + struct.calcsize( + tiff_header_struct + ), # StripOffsets, LONG, 1, length of header + 278, + 4, + 1, + params.rows, # RowsPerStrip, LONG, 1, length + 279, + 4, + 1, + img_size, # StripByteCounts, LONG, 1, size of image + 0, # last IFD + ) + + return tiff_header + data + + +def decode_stream_data(stream: Any) -> bytes: # utils.StreamObject + """ + Decode the stream data based on the specified filters. + + This function decodes the stream data using the filters provided in the + stream. It supports various filter types, including FlateDecode, + ASCIIHexDecode, RunLengthDecode, LZWDecode, ASCII85Decode, DCTDecode, JPXDecode, and + CCITTFaxDecode. + + Args: + stream: The input stream object containing the data and filters. + + Returns: + The decoded stream data. + + Raises: + NotImplementedError: If an unsupported filter type is encountered. + """ + filters = stream.get(SA.FILTER, ()) + if isinstance(filters, IndirectObject): + filters = cast(ArrayObject, filters.get_object()) + if not isinstance(filters, ArrayObject): + # we have a single filter instance + filters = (filters,) + decodparms = stream.get(SA.DECODE_PARMS, ({},) * len(filters)) + if not isinstance(decodparms, (list, tuple)): + decodparms = (decodparms,) + data: bytes = stream._data + # If there is not data to decode we should not try to decode the data. + if data: + for filter_type, params in zip(filters, decodparms): + if isinstance(params, NullObject): + params = {} + if filter_type in (FT.FLATE_DECODE, FTA.FL): + data = FlateDecode.decode(data, params) + elif filter_type in (FT.ASCII_HEX_DECODE, FTA.AHx): + data = ASCIIHexDecode.decode(data) + elif filter_type in (FT.RUN_LENGTH_DECODE, FTA.RL): + data = RunLengthDecode.decode(data) + elif filter_type in (FT.LZW_DECODE, FTA.LZW): + data = LZWDecode._decodeb(data, params) + elif filter_type in (FT.ASCII_85_DECODE, FTA.A85): + data = ASCII85Decode.decode(data) + elif filter_type == FT.DCT_DECODE: + data = DCTDecode.decode(data) + elif filter_type == FT.JPX_DECODE: + data = JPXDecode.decode(data) + elif filter_type == FT.CCITT_FAX_DECODE: + height = stream.get(IA.HEIGHT, ()) + data = CCITTFaxDecode.decode(data, params, height) + elif filter_type == "/Crypt": + if "/Name" in params or "/Type" in params: + raise NotImplementedError( + "/Crypt filter with /Name or /Type not supported yet" + ) + else: + # Unsupported filter + raise NotImplementedError(f"unsupported filter {filter_type}") + return data + + +def _xobj_to_image(x_object_obj: Dict[str, Any]) -> Tuple[Optional[str], bytes, Any]: + """ + Users need to have the pillow package installed. + + It's unclear if pypdf will keep this function here, hence it's private. + It might get removed at any point. + + Args: + x_object_obj: + + Returns: + Tuple[file extension, bytes, PIL.Image.Image] + """ + from ._xobj_image_helpers import ( + Image, + UnidentifiedImageError, + _extended_image_frombytes, + _get_imagemode, + _handle_flate, + _handle_jpx, + mode_str_type, + ) + + # for error reporting + if x_object_obj is None: # pragma: no cover + obj_as_text = x_object_obj.indirect_reference.__repr__() + else: + obj_as_text = x_object_obj.__repr__() + + size = (cast(int, x_object_obj[IA.WIDTH]), cast(int, x_object_obj[IA.HEIGHT])) + data = x_object_obj.get_data() # type: ignore + if isinstance(data, str): # pragma: no cover + data = data.encode() + if len(data) % (size[0] * size[1]) == 1 and data[-1] == 0x0A: # ie. '\n' + data = data[:-1] + colors = x_object_obj.get("/Colors", 1) + color_space: Any = x_object_obj.get("/ColorSpace", NullObject()).get_object() + if isinstance(color_space, list) and len(color_space) == 1: + color_space = color_space[0].get_object() + if ( + IA.COLOR_SPACE in x_object_obj + and x_object_obj[IA.COLOR_SPACE] == ColorSpaces.DEVICE_RGB + ): + # https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes + mode: mode_str_type = "RGB" + if x_object_obj.get("/BitsPerComponent", 8) < 8: + mode, invert_color = _get_imagemode( + f"{x_object_obj.get('/BitsPerComponent', 8)}bit", 0, "" + ) + else: + mode, invert_color = _get_imagemode( + color_space, + 2 + if ( + colors == 1 + and ( + not isinstance(color_space, NullObject) + and "Gray" not in color_space + ) + ) + else colors, + "", + ) + extension = None + alpha = None + filters = x_object_obj.get(SA.FILTER, NullObject()).get_object() + lfilters = filters[-1] if isinstance(filters, list) else filters + if lfilters in (FT.FLATE_DECODE, FT.RUN_LENGTH_DECODE): + img, image_format, extension, _ = _handle_flate( + size, + data, + mode, + color_space, + colors, + obj_as_text, + ) + elif lfilters in (FT.LZW_DECODE, FT.ASCII_85_DECODE, FT.CCITT_FAX_DECODE): + # I'm not sure if the following logic is correct. + # There might not be any relationship between the filters and the + # extension + if lfilters in (FT.LZW_DECODE, FT.CCITT_FAX_DECODE): + extension = ".tiff" # mime_type = "image/tiff" + image_format = "TIFF" + else: + extension = ".png" # mime_type = "image/png" + image_format = "PNG" + try: + img = Image.open(BytesIO(data), formats=("TIFF", "PNG")) + except UnidentifiedImageError: + img = _extended_image_frombytes(mode, size, data) + elif lfilters == FT.DCT_DECODE: + img, image_format, extension = Image.open(BytesIO(data)), "JPEG", ".jpg" + # invert_color kept unchanged + elif lfilters == FT.JPX_DECODE: + img, image_format, extension, invert_color = _handle_jpx( + size, data, mode, color_space, colors + ) + elif lfilters == FT.CCITT_FAX_DECODE: + img, image_format, extension, invert_color = ( + Image.open(BytesIO(data), formats=("TIFF",)), + "TIFF", + ".tiff", + False, + ) + elif mode == "CMYK": + img, image_format, extension, invert_color = ( + _extended_image_frombytes(mode, size, data), + "TIFF", + ".tif", + False, + ) + elif mode == "": + raise PdfReadError(f"ColorSpace field not found in {x_object_obj}") + else: + img, image_format, extension, invert_color = ( + _extended_image_frombytes(mode, size, data), + "PNG", + ".png", + False, + ) + # CMYK image and other colorspaces without decode + # requires reverting scale (cf p243,2§ last sentence) + decode = x_object_obj.get( + IA.DECODE, + ([1.0, 0.0] * len(img.getbands())) + if ( + (img.mode == "CMYK" and lfilters in (FT.DCT_DECODE, FT.JPX_DECODE)) + or (invert_color and img.mode == "L") + ) + else None, + ) + if ( + isinstance(color_space, ArrayObject) + and color_space[0].get_object() == "/Indexed" + ): + decode = None # decode is meanless of Indexed + if ( + isinstance(color_space, ArrayObject) + and color_space[0].get_object() == "/Separation" + ): + decode = [1.0, 0.0] * len(img.getbands()) + if decode is not None and not all(decode[i] == i % 2 for i in range(len(decode))): + lut: List[int] = [] + for i in range(0, len(decode), 2): + dmin = decode[i] + dmax = decode[i + 1] + lut.extend( + round(255.0 * (j / 255.0 * (dmax - dmin) + dmin)) for j in range(256) + ) + img = img.point(lut) + + if IA.S_MASK in x_object_obj: # add alpha channel + alpha = _xobj_to_image(x_object_obj[IA.S_MASK])[2] + if img.size != alpha.size: + logger_warning(f"image and mask size not matching: {obj_as_text}", __name__) + else: + # TODO : implement mask + if alpha.mode != "L": + alpha = alpha.convert("L") + if img.mode == "P": + img = img.convert("RGB") + elif img.mode == "1": + img = img.convert("L") + img.putalpha(alpha) + if "JPEG" in image_format: + extension = ".jp2" + image_format = "JPEG2000" + else: + extension = ".png" + image_format = "PNG" + + img_byte_arr = BytesIO() + try: + img.save(img_byte_arr, format=image_format) + except OSError: # pragma: no cover # covered with pillow 10.3 + # in case of we convert to RGBA and then to PNG + img1 = img.convert("RGBA") + image_format = "PNG" + extension = ".png" + img_byte_arr = BytesIO() + img1.save(img_byte_arr, format=image_format) + data = img_byte_arr.getvalue() + + try: # temporary try/except until other fixes of images + img = Image.open(BytesIO(data)) + except Exception: + img = None # type: ignore + return extension, data, img diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__init__.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__init__.py new file mode 100644 index 00000000..d9b0ea48 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__init__.py @@ -0,0 +1,242 @@ +# Copyright (c) 2006, Mathieu Fenniak +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +"""Implementation of generic PDF objects (dictionary, number, string, ...).""" +__author__ = "Mathieu Fenniak" +__author_email__ = "biziqe@mathieu.fenniak.net" + +from typing import List, Optional, Tuple, Union + +from .._utils import ( + deprecation_with_replacement, +) +from ..constants import OutlineFontFlag +from ._base import ( + BooleanObject, + ByteStringObject, + FloatObject, + IndirectObject, + NameObject, + NullObject, + NumberObject, + PdfObject, + TextStringObject, + encode_pdfdocencoding, + is_null_or_none, +) +from ._data_structures import ( + ArrayObject, + ContentStream, + DecodedStreamObject, + Destination, + DictionaryObject, + EncodedStreamObject, + Field, + StreamObject, + TreeObject, + read_object, +) +from ._fit import Fit +from ._outline import OutlineItem +from ._rectangle import RectangleObject +from ._utils import ( + create_string_object, + decode_pdfdocencoding, + hex_to_rgb, + read_hex_string_from_stream, + read_string_from_stream, +) +from ._viewerpref import ViewerPreferences + +PAGE_FIT = Fit.fit() + + +class AnnotationBuilder: # deprecated + """ + The AnnotationBuilder is deprecated. + + Instead, use the annotation classes in pypdf.annotations. + + See `adding PDF annotations <../user/adding-pdf-annotations.html>`_ for + its usage combined with PdfWriter. + """ + + from ..generic._rectangle import RectangleObject + + @staticmethod + def text( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + text: str, + open: bool = False, + flags: int = 0, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.text", "pypdf.annotations.Text", "4.0.0" + ) + + @staticmethod + def free_text( + text: str, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + font: str = "Helvetica", + bold: bool = False, + italic: bool = False, + font_size: str = "14pt", + font_color: str = "000000", + border_color: Optional[str] = "000000", + background_color: Optional[str] = "ffffff", + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.free_text", "pypdf.annotations.FreeText", "4.0.0" + ) + + @staticmethod + def popup( + *, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + flags: int = 0, + parent: Optional[DictionaryObject] = None, + open: bool = False, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.popup", "pypdf.annotations.Popup", "4.0.0" + ) + + @staticmethod + def line( + p1: Tuple[float, float], + p2: Tuple[float, float], + rect: Union[RectangleObject, Tuple[float, float, float, float]], + text: str = "", + title_bar: Optional[str] = None, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.line", "pypdf.annotations.Line", "4.0.0" + ) + + @staticmethod + def polyline( + vertices: List[Tuple[float, float]], + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.polyline", "pypdf.annotations.PolyLine", "4.0.0" + ) + + @staticmethod + def rectangle( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + interiour_color: Optional[str] = None, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.rectangle", "pypdf.annotations.Rectangle", "4.0.0" + ) + + @staticmethod + def highlight( + *, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + quad_points: ArrayObject, + highlight_color: str = "ff0000", + printing: bool = False, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.highlight", "pypdf.annotations.Highlight", "4.0.0" + ) + + @staticmethod + def ellipse( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + interiour_color: Optional[str] = None, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.ellipse", "pypdf.annotations.Ellipse", "4.0.0" + ) + + @staticmethod + def polygon(vertices: List[Tuple[float, float]]) -> None: + deprecation_with_replacement( + "AnnotationBuilder.polygon", "pypdf.annotations.Polygon", "4.0.0" + ) + + from ._fit import DEFAULT_FIT + + @staticmethod + def link( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + border: Optional[ArrayObject] = None, + url: Optional[str] = None, + target_page_index: Optional[int] = None, + fit: Fit = DEFAULT_FIT, + ) -> None: + deprecation_with_replacement( + "AnnotationBuilder.link", "pypdf.annotations.Link", "4.0.0" + ) + + +__all__ = [ + # Base types + "BooleanObject", + "FloatObject", + "NumberObject", + "NameObject", + "IndirectObject", + "NullObject", + "PdfObject", + "TextStringObject", + "ByteStringObject", + # Annotations + "AnnotationBuilder", + # Fit + "Fit", + "PAGE_FIT", + # Data structures + "ArrayObject", + "DictionaryObject", + "TreeObject", + "StreamObject", + "DecodedStreamObject", + "EncodedStreamObject", + "ContentStream", + "RectangleObject", + "Field", + "Destination", + "ViewerPreferences", + # --- More specific stuff + # Outline + "OutlineItem", + "OutlineFontFlag", + # Data structures core functions + "read_object", + # Utility functions + "create_string_object", + "encode_pdfdocencoding", + "decode_pdfdocencoding", + "hex_to_rgb", + "is_null_or_none", + "read_hex_string_from_stream", + "read_string_from_stream", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/__init__.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..13e981e1 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/__init__.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_base.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_base.cpython-38.pyc new file mode 100644 index 00000000..df2e8fc0 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_base.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_data_structures.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_data_structures.cpython-38.pyc new file mode 100644 index 00000000..11c726ec Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_data_structures.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_fit.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_fit.cpython-38.pyc new file mode 100644 index 00000000..da5b7009 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_fit.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_image_inline.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_image_inline.cpython-38.pyc new file mode 100644 index 00000000..9495a60f Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_image_inline.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_outline.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_outline.cpython-38.pyc new file mode 100644 index 00000000..94c4cc3e Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_outline.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_rectangle.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_rectangle.cpython-38.pyc new file mode 100644 index 00000000..6f775c1b Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_rectangle.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_utils.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_utils.cpython-38.pyc new file mode 100644 index 00000000..7debe184 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_utils.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_viewerpref.cpython-38.pyc b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_viewerpref.cpython-38.pyc new file mode 100644 index 00000000..c6e67880 Binary files /dev/null and b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/__pycache__/_viewerpref.cpython-38.pyc differ diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_base.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_base.py new file mode 100644 index 00000000..fd7d1a8f --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_base.py @@ -0,0 +1,855 @@ +# Copyright (c) 2006, Mathieu Fenniak +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +import binascii +import codecs +import hashlib +import re +from binascii import unhexlify +from math import log10 +from struct import iter_unpack +from typing import Any, Callable, ClassVar, Dict, Optional, Sequence, Union, cast + +from .._codecs import _pdfdoc_encoding_rev +from .._protocols import PdfObjectProtocol, PdfWriterProtocol +from .._utils import ( + StreamType, + deprecate_no_replacement, + logger_warning, + read_non_whitespace, + read_until_regex, +) +from ..errors import STREAM_TRUNCATED_PREMATURELY, PdfReadError, PdfStreamError + +__author__ = "Mathieu Fenniak" +__author_email__ = "biziqe@mathieu.fenniak.net" + + +class PdfObject(PdfObjectProtocol): + # function for calculating a hash value + hash_func: Callable[..., "hashlib._Hash"] = hashlib.sha1 + indirect_reference: Optional["IndirectObject"] + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + raise NotImplementedError( + f"{self.__class__.__name__} does not implement .hash_bin() so far" + ) + + def hash_value_data(self) -> bytes: + return ("%s" % self).encode() + + def hash_value(self) -> bytes: + return ( + "%s:%s" + % ( + self.__class__.__name__, + self.hash_func(self.hash_value_data()).hexdigest(), + ) + ).encode() + + def clone( + self, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "PdfObject": + """ + Clone object into pdf_dest (PdfWriterProtocol which is an interface for PdfWriter). + + By default, this method will call ``_reference_clone`` (see ``_reference``). + + + Args: + pdf_dest: Target to clone to. + force_duplicate: By default, if the object has already been cloned and referenced, + the copy will be returned; when ``True``, a new copy will be created. + (Default value = ``False``) + ignore_fields: List/tuple of field names (for dictionaries) that will be ignored + during cloning (applies to children duplication as well). If fields are to be + considered for a limited number of levels, you have to add it as integer, for + example ``[1,"/B","/TOTO"]`` means that ``"/B"`` will be ignored at the first + level only but ``"/TOTO"`` on all levels. + + Returns: + The cloned PdfObject + """ + raise NotImplementedError( + f"{self.__class__.__name__} does not implement .clone so far" + ) + + def _reference_clone( + self, clone: Any, pdf_dest: PdfWriterProtocol, force_duplicate: bool = False + ) -> PdfObjectProtocol: + """ + Reference the object within the _objects of pdf_dest only if + indirect_reference attribute exists (which means the objects was + already identified in xref/xobjstm) if object has been already + referenced do nothing. + + Args: + clone: + pdf_dest: + + Returns: + The clone + """ + try: + if not force_duplicate and clone.indirect_reference.pdf == pdf_dest: + return clone + except Exception: + pass + # if hasattr(clone, "indirect_reference"): + try: + ind = self.indirect_reference + except AttributeError: + return clone + if ( + pdf_dest.incremental + and ind is not None + and ind.pdf == pdf_dest._reader + and ind.idnum <= len(pdf_dest._objects) + ): + i = ind.idnum + else: + i = len(pdf_dest._objects) + 1 + if ind is not None: + if id(ind.pdf) not in pdf_dest._id_translated: + pdf_dest._id_translated[id(ind.pdf)] = {} + pdf_dest._id_translated[id(ind.pdf)]["PreventGC"] = ind.pdf # type: ignore + if ( + not force_duplicate + and ind.idnum in pdf_dest._id_translated[id(ind.pdf)] + ): + obj = pdf_dest.get_object( + pdf_dest._id_translated[id(ind.pdf)][ind.idnum] + ) + assert obj is not None + return obj + pdf_dest._id_translated[id(ind.pdf)][ind.idnum] = i + try: + pdf_dest._objects[i - 1] = clone + except IndexError: + pdf_dest._objects.append(clone) + i = len(pdf_dest._objects) + clone.indirect_reference = IndirectObject(i, 0, pdf_dest) + return clone + + def get_object(self) -> Optional["PdfObject"]: + """Resolve indirect references.""" + return self + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + raise NotImplementedError + + +class NullObject(PdfObject): + def clone( + self, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "NullObject": + """Clone object into pdf_dest.""" + return cast( + "NullObject", self._reference_clone(NullObject(), pdf_dest, force_duplicate) + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__,)) + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(b"null") + + @staticmethod + def read_from_stream(stream: StreamType) -> "NullObject": + nulltxt = stream.read(4) + if nulltxt != b"null": + raise PdfReadError("Could not read Null object") + return NullObject() + + def __repr__(self) -> str: + return "NullObject" + + +def is_null_or_none(x: Any) -> bool: + """ + Returns: + True if x is None or NullObject. + """ + return x is None or ( + isinstance(x, PdfObject) and isinstance(x.get_object(), NullObject) + ) + + +class BooleanObject(PdfObject): + def __init__(self, value: Any) -> None: + self.value = value + + def clone( + self, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "BooleanObject": + """Clone object into pdf_dest.""" + return cast( + "BooleanObject", + self._reference_clone(BooleanObject(self.value), pdf_dest, force_duplicate), + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, self.value)) + + def __eq__(self, __o: object) -> bool: + if isinstance(__o, BooleanObject): + return self.value == __o.value + elif isinstance(__o, bool): + return self.value == __o + else: + return False + + def __repr__(self) -> str: + return "True" if self.value else "False" + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + if self.value: + stream.write(b"true") + else: + stream.write(b"false") + + @staticmethod + def read_from_stream(stream: StreamType) -> "BooleanObject": + word = stream.read(4) + if word == b"true": + return BooleanObject(True) + elif word == b"fals": + stream.read(1) + return BooleanObject(False) + else: + raise PdfReadError("Could not read Boolean object") + + +class IndirectObject(PdfObject): + def __init__(self, idnum: int, generation: int, pdf: Any) -> None: # PdfReader + self.idnum = idnum + self.generation = generation + self.pdf = pdf + + def __hash__(self) -> int: + return hash((self.idnum, self.generation, id(self.pdf))) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, self.idnum, self.generation, id(self.pdf))) + + def clone( + self, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "IndirectObject": + """Clone object into pdf_dest.""" + if self.pdf == pdf_dest and not force_duplicate: + # Already duplicated and no extra duplication required + return self + if id(self.pdf) not in pdf_dest._id_translated: + pdf_dest._id_translated[id(self.pdf)] = {} + + if self.idnum in pdf_dest._id_translated[id(self.pdf)]: + dup = pdf_dest.get_object(pdf_dest._id_translated[id(self.pdf)][self.idnum]) + if force_duplicate: + assert dup is not None + assert dup.indirect_reference is not None + idref = dup.indirect_reference + return IndirectObject(idref.idnum, idref.generation, idref.pdf) + else: + obj = self.get_object() + # case observed : a pointed object can not be found + if obj is None: + # this normally + obj = NullObject() + assert isinstance(self, (IndirectObject,)) + obj.indirect_reference = self + dup = pdf_dest._add_object( + obj.clone(pdf_dest, force_duplicate, ignore_fields) + ) + # asserts added to prevent errors in mypy + assert dup is not None + assert dup.indirect_reference is not None + return dup.indirect_reference + + @property + def indirect_reference(self) -> "IndirectObject": # type: ignore[override] + return self + + def get_object(self) -> Optional["PdfObject"]: + return self.pdf.get_object(self) + + def __deepcopy__(self, memo: Any) -> "IndirectObject": + return IndirectObject(self.idnum, self.generation, self.pdf) + + def _get_object_with_check(self) -> Optional["PdfObject"]: + o = self.get_object() + # the check is done here to not slow down get_object() + if isinstance(o, IndirectObject): + raise PdfStreamError( + f"{self.__repr__()} references an IndirectObject {o.__repr__()}" + ) + return o + + def __getattr__(self, name: str) -> Any: + # Attribute not found in object: look in pointed object + try: + return getattr(self._get_object_with_check(), name) + except AttributeError: + raise AttributeError( + f"No attribute {name} found in IndirectObject or pointed object" + ) + + def __getitem__(self, key: Any) -> Any: + # items should be extracted from pointed Object + return self._get_object_with_check()[key] # type: ignore + + def __float__(self) -> str: + # in this case we are looking for the pointed data + return self.get_object().__float__() # type: ignore + + def __str__(self) -> str: + # in this case we are looking for the pointed data + return self.get_object().__str__() + + def __repr__(self) -> str: + return f"IndirectObject({self.idnum!r}, {self.generation!r}, {id(self.pdf)})" + + def __eq__(self, other: object) -> bool: + return ( + other is not None + and isinstance(other, IndirectObject) + and self.idnum == other.idnum + and self.generation == other.generation + and self.pdf is other.pdf + ) + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(f"{self.idnum} {self.generation} R".encode()) + + @staticmethod + def read_from_stream(stream: StreamType, pdf: Any) -> "IndirectObject": # PdfReader + idnum = b"" + while True: + tok = stream.read(1) + if not tok: + raise PdfStreamError(STREAM_TRUNCATED_PREMATURELY) + if tok.isspace(): + break + idnum += tok + generation = b"" + while True: + tok = stream.read(1) + if not tok: + raise PdfStreamError(STREAM_TRUNCATED_PREMATURELY) + if tok.isspace(): + if not generation: + continue + break + generation += tok + r = read_non_whitespace(stream) + if r != b"R": + raise PdfReadError( + f"Error reading indirect object reference at byte {hex(stream.tell())}" + ) + return IndirectObject(int(idnum), int(generation), pdf) + + +FLOAT_WRITE_PRECISION = 8 # shall be min 5 digits max, allow user adj + + +class FloatObject(float, PdfObject): + def __new__( + cls, value: Any = "0.0", context: Optional[Any] = None + ) -> "FloatObject": + try: + value = float(value) + return float.__new__(cls, value) + except Exception as e: + # If this isn't a valid decimal (happens in malformed PDFs) + # fallback to 0 + logger_warning( + f"{e} : FloatObject ({value}) invalid; use 0.0 instead", __name__ + ) + return float.__new__(cls, 0.0) + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "FloatObject": + """Clone object into pdf_dest.""" + return cast( + "FloatObject", + self._reference_clone(FloatObject(self), pdf_dest, force_duplicate), + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, self.as_numeric)) + + def myrepr(self) -> str: + if self == 0: + return "0.0" + nb = FLOAT_WRITE_PRECISION - int(log10(abs(self))) + s = f"{self:.{max(1,nb)}f}".rstrip("0").rstrip(".") + return s + + def __repr__(self) -> str: + return self.myrepr() # repr(float(self)) + + def as_numeric(self) -> float: + return float(self) + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(self.myrepr().encode("utf8")) + + +class NumberObject(int, PdfObject): + NumberPattern = re.compile(b"[^+-.0-9]") + + def __new__(cls, value: Any) -> "NumberObject": + try: + return int.__new__(cls, int(value)) + except ValueError: + logger_warning(f"NumberObject({value}) invalid; use 0 instead", __name__) + return int.__new__(cls, 0) + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "NumberObject": + """Clone object into pdf_dest.""" + return cast( + "NumberObject", + self._reference_clone(NumberObject(self), pdf_dest, force_duplicate), + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, self.as_numeric())) + + def as_numeric(self) -> int: + return int(repr(self).encode("utf8")) + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(repr(self).encode("utf8")) + + @staticmethod + def read_from_stream(stream: StreamType) -> Union["NumberObject", "FloatObject"]: + num = read_until_regex(stream, NumberObject.NumberPattern) + if num.find(b".") != -1: + return FloatObject(num) + return NumberObject(num) + + +class ByteStringObject(bytes, PdfObject): + """ + Represents a string object where the text encoding could not be determined. + + This occurs quite often, as the PDF spec doesn't provide an alternate way to + represent strings -- for example, the encryption data stored in files (like + /O) is clearly not text, but is still stored in a "String" object. + """ + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "ByteStringObject": + """Clone object into pdf_dest.""" + return cast( + "ByteStringObject", + self._reference_clone( + ByteStringObject(bytes(self)), pdf_dest, force_duplicate + ), + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, bytes(self))) + + @property + def original_bytes(self) -> bytes: + """For compatibility with TextStringObject.original_bytes.""" + return self + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(b"<") + stream.write(binascii.hexlify(self)) + stream.write(b">") + + +class TextStringObject(str, PdfObject): # noqa: SLOT000 + """ + A string object that has been decoded into a real unicode string. + + If read from a PDF document, this string appeared to match the + PDFDocEncoding, or contained a UTF-16BE BOM mark to cause UTF-16 decoding + to occur. + """ + + autodetect_pdfdocencoding: bool + autodetect_utf16: bool + utf16_bom: bytes + _original_bytes: Optional[bytes] = None + + def __new__(cls, value: Any) -> "TextStringObject": + org = None + if isinstance(value, bytes): + org = value + value = value.decode("charmap") + o = str.__new__(cls, value) + o._original_bytes = org + o.autodetect_utf16 = False + o.autodetect_pdfdocencoding = False + o.utf16_bom = b"" + if value.startswith(("\xfe\xff", "\xff\xfe")): + assert org is not None # for mypy + try: + o = str.__new__(cls, org.decode("utf-16")) + except UnicodeDecodeError as exc: + logger_warning( + f"{exc!s}\ninitial string:{exc.object!r}", + __name__, + ) + o = str.__new__(cls, exc.object[: exc.start].decode("utf-16")) + o._original_bytes = org + o.autodetect_utf16 = True + o.utf16_bom = org[:2] + else: + try: + encode_pdfdocencoding(o) + o.autodetect_pdfdocencoding = True + except UnicodeEncodeError: + o.autodetect_utf16 = True + o.utf16_bom = codecs.BOM_UTF16_BE + return o + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "TextStringObject": + """Clone object into pdf_dest.""" + obj = TextStringObject(self) + obj._original_bytes = self._original_bytes + obj.autodetect_pdfdocencoding = self.autodetect_pdfdocencoding + obj.autodetect_utf16 = self.autodetect_utf16 + obj.utf16_bom = self.utf16_bom + return cast( + "TextStringObject", self._reference_clone(obj, pdf_dest, force_duplicate) + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, self.original_bytes)) + + @property + def original_bytes(self) -> bytes: + """ + It is occasionally possible that a text string object gets created where + a byte string object was expected due to the autodetection mechanism -- + if that occurs, this "original_bytes" property can be used to + back-calculate what the original encoded bytes were. + """ + if self._original_bytes is not None: + return self._original_bytes + else: + return self.get_original_bytes() + + def get_original_bytes(self) -> bytes: + # We're a text string object, but the library is trying to get our raw + # bytes. This can happen if we auto-detected this string as text, but + # we were wrong. It's pretty common. Return the original bytes that + # would have been used to create this object, based upon the autodetect + # method. + if self.autodetect_utf16: + if self.utf16_bom == codecs.BOM_UTF16_LE: + return codecs.BOM_UTF16_LE + self.encode("utf-16le") + elif self.utf16_bom == codecs.BOM_UTF16_BE: + return codecs.BOM_UTF16_BE + self.encode("utf-16be") + else: + return self.encode("utf-16be") + elif self.autodetect_pdfdocencoding: + return encode_pdfdocencoding(self) + else: + raise Exception("no information about original bytes") # pragma: no cover + + def get_encoded_bytes(self) -> bytes: + # Try to write the string out as a PDFDocEncoding encoded string. It's + # nicer to look at in the PDF file. Sadly, we take a performance hit + # here for trying... + try: + if self._original_bytes is not None: + return self._original_bytes + if self.autodetect_utf16: + raise UnicodeEncodeError("", "forced", -1, -1, "") + bytearr = encode_pdfdocencoding(self) + except UnicodeEncodeError: + if self.utf16_bom == codecs.BOM_UTF16_LE: + bytearr = codecs.BOM_UTF16_LE + self.encode("utf-16le") + elif self.utf16_bom == codecs.BOM_UTF16_BE: + bytearr = codecs.BOM_UTF16_BE + self.encode("utf-16be") + else: + bytearr = self.encode("utf-16be") + return bytearr + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + bytearr = self.get_encoded_bytes() + stream.write(b"(") + for c_ in iter_unpack("c", bytearr): + c = cast(bytes, c_[0]) + if not c.isalnum() and c != b" ": + # This: + # stream.write(rf"\{c:0>3o}".encode()) + # gives + # https://github.com/davidhalter/parso/issues/207 + stream.write(b"\\%03o" % ord(c)) + else: + stream.write(c) + stream.write(b")") + + +class NameObject(str, PdfObject): # noqa: SLOT000 + delimiter_pattern = re.compile(rb"\s+|[\(\)<>\[\]{}/%]") + surfix = b"/" + renumber_table: ClassVar[Dict[str, bytes]] = { + "#": b"#23", + "(": b"#28", + ")": b"#29", + "/": b"#2F", + "%": b"#25", + **{chr(i): f"#{i:02X}".encode() for i in range(33)}, + } + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "NameObject": + """Clone object into pdf_dest.""" + return cast( + "NameObject", + self._reference_clone(NameObject(self), pdf_dest, force_duplicate), + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, self)) + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(self.renumber()) + + def renumber(self) -> bytes: + out = self[0].encode("utf-8") + if out != b"/": + deprecate_no_replacement( + f"Incorrect first char in NameObject, should start with '/': ({self})", + "6.0.0", + ) + for c in self[1:]: + if c > "~": + for x in c.encode("utf-8"): + out += f"#{x:02X}".encode() + else: + try: + out += self.renumber_table[c] + except KeyError: + out += c.encode("utf-8") + return out + + @staticmethod + def unnumber(sin: bytes) -> bytes: + i = sin.find(b"#", 0) + while i >= 0: + try: + sin = sin[:i] + unhexlify(sin[i + 1 : i + 3]) + sin[i + 3 :] + i = sin.find(b"#", i + 1) + except ValueError: + # if the 2 characters after # can not be converted to hex + # we change nothing and carry on + i = i + 1 + return sin + + CHARSETS = ("utf-8", "gbk", "latin1") + + @staticmethod + def read_from_stream(stream: StreamType, pdf: Any) -> "NameObject": # PdfReader + name = stream.read(1) + if name != NameObject.surfix: + raise PdfReadError("name read error") + name += read_until_regex(stream, NameObject.delimiter_pattern) + try: + # Name objects should represent irregular characters + # with a '#' followed by the symbol's hex number + name = NameObject.unnumber(name) + for enc in NameObject.CHARSETS: + try: + ret = name.decode(enc) + return NameObject(ret) + except Exception: + pass + raise UnicodeDecodeError("", name, 0, 0, "Code Not Found") + except (UnicodeEncodeError, UnicodeDecodeError) as e: + if not pdf.strict: + logger_warning( + f"Illegal character in NameObject ({name!r}), " + "you may need to adjust NameObject.CHARSETS", + __name__, + ) + return NameObject(name.decode("charmap")) + else: + raise PdfReadError( + f"Illegal character in NameObject ({name!r}). " + "You may need to adjust NameObject.CHARSETS.", + ) from e + + +def encode_pdfdocencoding(unicode_string: str) -> bytes: + try: + return bytes([_pdfdoc_encoding_rev[k] for k in unicode_string]) + except KeyError: + raise UnicodeEncodeError( + "pdfdocencoding", + unicode_string, + -1, + -1, + "does not exist in translation table", + ) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_data_structures.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_data_structures.py new file mode 100644 index 00000000..cc4b4a03 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_data_structures.py @@ -0,0 +1,1640 @@ +# Copyright (c) 2006, Mathieu Fenniak +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +__author__ = "Mathieu Fenniak" +__author_email__ = "biziqe@mathieu.fenniak.net" + +import logging +import re +import sys +from io import BytesIO +from math import ceil +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Sequence, + Set, + Tuple, + Union, + cast, +) + +from .._protocols import PdfReaderProtocol, PdfWriterProtocol, XmpInformationProtocol +from .._utils import ( + WHITESPACES, + StreamType, + deprecation_no_replacement, + deprecation_with_replacement, + logger_warning, + read_non_whitespace, + read_until_regex, + skip_over_comment, +) +from ..constants import ( + CheckboxRadioButtonAttributes, + FieldDictionaryAttributes, + OutlineFontFlag, +) +from ..constants import FilterTypes as FT +from ..constants import StreamAttributes as SA +from ..constants import TypArguments as TA +from ..constants import TypFitArguments as TF +from ..errors import STREAM_TRUNCATED_PREMATURELY, PdfReadError, PdfStreamError +from ._base import ( + BooleanObject, + ByteStringObject, + FloatObject, + IndirectObject, + NameObject, + NullObject, + NumberObject, + PdfObject, + TextStringObject, + is_null_or_none, +) +from ._fit import Fit +from ._image_inline import ( + extract_inline_A85, + extract_inline_AHx, + extract_inline_DCT, + extract_inline_default, + extract_inline_RL, +) +from ._utils import read_hex_string_from_stream, read_string_from_stream + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +logger = logging.getLogger(__name__) +NumberSigns = b"+-" +IndirectPattern = re.compile(rb"[+-]?(\d+)\s+(\d+)\s+R[^a-zA-Z]") + + +class ArrayObject(List[Any], PdfObject): + def clone( + self, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "ArrayObject": + """Clone object into pdf_dest.""" + try: + if self.indirect_reference.pdf == pdf_dest and not force_duplicate: # type: ignore + return self + except Exception: + pass + arr = cast( + "ArrayObject", + self._reference_clone(ArrayObject(), pdf_dest, force_duplicate), + ) + for data in self: + if isinstance(data, StreamObject): + dup = data._reference_clone( + data.clone(pdf_dest, force_duplicate, ignore_fields), + pdf_dest, + force_duplicate, + ) + arr.append(dup.indirect_reference) + elif hasattr(data, "clone"): + arr.append(data.clone(pdf_dest, force_duplicate, ignore_fields)) + else: + arr.append(data) + return arr + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash((self.__class__, tuple(x.hash_bin() for x in self))) + + def items(self) -> Iterable[Any]: + """Emulate DictionaryObject.items for a list (index, object).""" + return enumerate(self) + + def _to_lst(self, lst: Any) -> List[Any]: + # Convert to list, internal + if isinstance(lst, (list, tuple, set)): + pass + elif isinstance(lst, PdfObject): + lst = [lst] + elif isinstance(lst, str): + if lst[0] == "/": + lst = [NameObject(lst)] + else: + lst = [TextStringObject(lst)] + elif isinstance(lst, bytes): + lst = [ByteStringObject(lst)] + else: # for numbers,... + lst = [lst] + return lst + + def __add__(self, lst: Any) -> "ArrayObject": + """ + Allow extension by adding list or add one element only + + Args: + lst: any list, tuples are extended the list. + other types(numbers,...) will be appended. + if str is passed it will be converted into TextStringObject + or NameObject (if starting with "/") + if bytes is passed it will be converted into ByteStringObject + + Returns: + ArrayObject with all elements + """ + temp = ArrayObject(self) + temp.extend(self._to_lst(lst)) + return temp + + def __iadd__(self, lst: Any) -> Self: + """ + Allow extension by adding list or add one element only + + Args: + lst: any list, tuples are extended the list. + other types(numbers,...) will be appended. + if str is passed it will be converted into TextStringObject + or NameObject (if starting with "/") + if bytes is passed it will be converted into ByteStringObject + """ + self.extend(self._to_lst(lst)) + return self + + def __isub__(self, lst: Any) -> Self: + """Allow to remove items""" + for x in self._to_lst(lst): + try: + x = self.index(x) + del self[x] + except ValueError: + pass + return self + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecation_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(b"[") + for data in self: + stream.write(b" ") + data.write_to_stream(stream) + stream.write(b" ]") + + @staticmethod + def read_from_stream( + stream: StreamType, + pdf: Optional[PdfReaderProtocol], + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, + ) -> "ArrayObject": + arr = ArrayObject() + tmp = stream.read(1) + if tmp != b"[": + raise PdfReadError("Could not read array") + while True: + # skip leading whitespace + tok = stream.read(1) + while tok.isspace(): + tok = stream.read(1) + stream.seek(-1, 1) + # check for array ending + peek_ahead = stream.read(1) + if peek_ahead == b"]": + break + stream.seek(-1, 1) + # read and append obj + arr.append(read_object(stream, pdf, forced_encoding)) + return arr + + +class DictionaryObject(Dict[Any, Any], PdfObject): + def clone( + self, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "DictionaryObject": + """Clone object into pdf_dest.""" + try: + if self.indirect_reference.pdf == pdf_dest and not force_duplicate: # type: ignore + return self + except Exception: + pass + + visited: Set[Tuple[int, int]] = set() # (idnum, generation) + d__ = cast( + "DictionaryObject", + self._reference_clone(self.__class__(), pdf_dest, force_duplicate), + ) + if ignore_fields is None: + ignore_fields = [] + if len(d__.keys()) == 0: + d__._clone(self, pdf_dest, force_duplicate, ignore_fields, visited) + return d__ + + def _clone( + self, + src: "DictionaryObject", + pdf_dest: PdfWriterProtocol, + force_duplicate: bool, + ignore_fields: Optional[Sequence[Union[str, int]]], + visited: Set[Tuple[int, int]], # (idnum, generation) + ) -> None: + """ + Update the object from src. + + Args: + src: "DictionaryObject": + pdf_dest: + force_duplicate: + ignore_fields: + """ + # first we remove for the ignore_fields + # that are for a limited number of levels + x = 0 + assert ignore_fields is not None + ignore_fields = list(ignore_fields) + while x < len(ignore_fields): + if isinstance(ignore_fields[x], int): + if cast(int, ignore_fields[x]) <= 0: + del ignore_fields[x] + del ignore_fields[x] + continue + else: + ignore_fields[x] -= 1 # type:ignore + x += 1 + # First check if this is a chain list, we need to loop to prevent recur + if any( + field not in ignore_fields + and field in src + and isinstance(src.raw_get(field), IndirectObject) + and isinstance(src[field], DictionaryObject) + and ( + src.get("/Type", None) is None + or cast(DictionaryObject, src[field]).get("/Type", None) is None + or src.get("/Type", None) + == cast(DictionaryObject, src[field]).get("/Type", None) + ) + for field in ["/Next", "/Prev", "/N", "/V"] + ): + ignore_fields = list(ignore_fields) + for lst in (("/Next", "/Prev"), ("/N", "/V")): + for k in lst: + objs = [] + if ( + k in src + and k not in self + and isinstance(src.raw_get(k), IndirectObject) + and isinstance(src[k], DictionaryObject) + # IF need to go further the idea is to check + # that the types are the same: + and ( + src.get("/Type", None) is None + or cast(DictionaryObject, src[k]).get("/Type", None) is None + or src.get("/Type", None) + == cast(DictionaryObject, src[k]).get("/Type", None) + ) + ): + cur_obj: Optional[DictionaryObject] = cast( + "DictionaryObject", src[k] + ) + prev_obj: Optional[DictionaryObject] = self + while cur_obj is not None: + clon = cast( + "DictionaryObject", + cur_obj._reference_clone( + cur_obj.__class__(), pdf_dest, force_duplicate + ), + ) + # check to see if we've previously processed our item + if clon.indirect_reference is not None: + idnum = clon.indirect_reference.idnum + generation = clon.indirect_reference.generation + if (idnum, generation) in visited: + cur_obj = None + break + visited.add((idnum, generation)) + objs.append((cur_obj, clon)) + assert prev_obj is not None + prev_obj[NameObject(k)] = clon.indirect_reference + prev_obj = clon + try: + if cur_obj == src: + cur_obj = None + else: + cur_obj = cast("DictionaryObject", cur_obj[k]) + except Exception: + cur_obj = None + for s, c in objs: + c._clone( + s, pdf_dest, force_duplicate, ignore_fields, visited + ) + + for k, v in src.items(): + if k not in ignore_fields: + if isinstance(v, StreamObject): + if not hasattr(v, "indirect_reference"): + v.indirect_reference = None + vv = v.clone(pdf_dest, force_duplicate, ignore_fields) + assert vv.indirect_reference is not None + self[k.clone(pdf_dest)] = vv.indirect_reference # type: ignore[attr-defined] + elif k not in self: + self[NameObject(k)] = ( + v.clone(pdf_dest, force_duplicate, ignore_fields) + if hasattr(v, "clone") + else v + ) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + return hash( + (self.__class__, tuple(((k, v.hash_bin()) for k, v in self.items()))) + ) + + def raw_get(self, key: Any) -> Any: + return dict.__getitem__(self, key) + + def get_inherited(self, key: str, default: Any = None) -> Any: + """ + Returns the value of a key or from the parent if not found. + If not found returns default. + + Args: + key: string identifying the field to return + + default: default value to return + + Returns: + Current key or inherited one, otherwise default value. + """ + if key in self: + return self[key] + try: + if "/Parent" not in self: + return default + raise KeyError("not present") + except KeyError: + return cast("DictionaryObject", self["/Parent"].get_object()).get_inherited( + key, default + ) + + def __setitem__(self, key: Any, value: Any) -> Any: + if not isinstance(key, PdfObject): + raise ValueError("key must be PdfObject") + if not isinstance(value, PdfObject): + raise ValueError("value must be PdfObject") + return dict.__setitem__(self, key, value) + + def setdefault(self, key: Any, value: Optional[Any] = None) -> Any: + if not isinstance(key, PdfObject): + raise ValueError("key must be PdfObject") + if not isinstance(value, PdfObject): + raise ValueError("value must be PdfObject") + return dict.setdefault(self, key, value) # type: ignore + + def __getitem__(self, key: Any) -> PdfObject: + return dict.__getitem__(self, key).get_object() + + @property + def xmp_metadata(self) -> Optional[XmpInformationProtocol]: + """ + Retrieve XMP (Extensible Metadata Platform) data relevant to the this + object, if available. + + See Table 347 — Additional entries in a metadata stream dictionary. + + Returns: + Returns a :class:`~pypdf.xmp.XmpInformation` instance + that can be used to access XMP metadata from the document. Can also + return None if no metadata was found on the document root. + """ + from ..xmp import XmpInformation + + metadata = self.get("/Metadata", None) + if is_null_or_none(metadata): + return None + metadata = metadata.get_object() + + if not isinstance(metadata, XmpInformation): + metadata = XmpInformation(metadata) + self[NameObject("/Metadata")] = metadata + return metadata + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecation_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(b"<<\n") + for key, value in list(self.items()): + if len(key) > 2 and key[1] == "%" and key[-1] == "%": + continue + key.write_to_stream(stream, encryption_key) + stream.write(b" ") + value.write_to_stream(stream) + stream.write(b"\n") + stream.write(b">>") + + @staticmethod + def read_from_stream( + stream: StreamType, + pdf: Optional[PdfReaderProtocol], + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, + ) -> "DictionaryObject": + def get_next_obj_pos( + p: int, p1: int, rem_gens: List[int], pdf: PdfReaderProtocol + ) -> int: + out = p1 + for gen in rem_gens: + loc = pdf.xref[gen] + try: + out = min(out, min([x for x in loc.values() if p < x <= p1])) + except ValueError: + pass + return out + + def read_unsized_from_stream( + stream: StreamType, pdf: PdfReaderProtocol + ) -> bytes: + # we are just pointing at beginning of the stream + eon = get_next_obj_pos(stream.tell(), 2**32, list(pdf.xref), pdf) - 1 + curr = stream.tell() + rw = stream.read(eon - stream.tell()) + p = rw.find(b"endstream") + if p < 0: + raise PdfReadError( + f"Unable to find 'endstream' marker for obj starting at {curr}." + ) + stream.seek(curr + p + 9) + return rw[: p - 1] + + tmp = stream.read(2) + if tmp != b"<<": + raise PdfReadError( + f"Dictionary read error at byte {hex(stream.tell())}: " + "stream must begin with '<<'" + ) + data: Dict[Any, Any] = {} + while True: + tok = read_non_whitespace(stream) + if tok == b"\x00": + continue + elif tok == b"%": + stream.seek(-1, 1) + skip_over_comment(stream) + continue + if not tok: + raise PdfStreamError(STREAM_TRUNCATED_PREMATURELY) + + if tok == b">": + stream.read(1) + break + stream.seek(-1, 1) + try: + key = read_object(stream, pdf) + tok = read_non_whitespace(stream) + stream.seek(-1, 1) + value = read_object(stream, pdf, forced_encoding) + except Exception as exc: + if pdf is not None and pdf.strict: + raise PdfReadError(exc.__repr__()) + logger_warning(exc.__repr__(), __name__) + retval = DictionaryObject() + retval.update(data) + return retval # return partial data + + if not data.get(key): + data[key] = value + else: + # multiple definitions of key not permitted + msg = ( + f"Multiple definitions in dictionary at byte " + f"{hex(stream.tell())} for key {key}" + ) + if pdf is not None and pdf.strict: + raise PdfReadError(msg) + logger_warning(msg, __name__) + + pos = stream.tell() + s = read_non_whitespace(stream) + if s == b"s" and stream.read(5) == b"tream": + eol = stream.read(1) + # odd PDF file output has spaces after 'stream' keyword but before EOL. + # patch provided by Danial Sandler + while eol == b" ": + eol = stream.read(1) + if eol not in (b"\n", b"\r"): + raise PdfStreamError("Stream data must be followed by a newline") + if eol == b"\r" and stream.read(1) != b"\n": + stream.seek(-1, 1) + # this is a stream object, not a dictionary + if SA.LENGTH not in data: + if pdf is not None and pdf.strict: + raise PdfStreamError("Stream length not defined") + else: + logger_warning( + f"Stream length not defined @pos={stream.tell()}", __name__ + ) + data[NameObject(SA.LENGTH)] = NumberObject(-1) + length = data[SA.LENGTH] + if isinstance(length, IndirectObject): + t = stream.tell() + assert pdf is not None # hint for mypy + length = pdf.get_object(length) + stream.seek(t, 0) + if length is None: # if the PDF is damaged + length = -1 + pstart = stream.tell() + if length > 0: + data["__streamdata__"] = stream.read(length) + else: + data["__streamdata__"] = read_until_regex( + stream, re.compile(b"endstream") + ) + e = read_non_whitespace(stream) + ndstream = stream.read(8) + if (e + ndstream) != b"endstream": + # (sigh) - the odd PDF file has a length that is too long, so + # we need to read backwards to find the "endstream" ending. + # ReportLab (unknown version) generates files with this bug, + # and Python users into PDF files tend to be our audience. + # we need to do this to correct the streamdata and chop off + # an extra character. + pos = stream.tell() + stream.seek(-10, 1) + end = stream.read(9) + if end == b"endstream": + # we found it by looking back one character further. + data["__streamdata__"] = data["__streamdata__"][:-1] + elif pdf is not None and not pdf.strict: + stream.seek(pstart, 0) + data["__streamdata__"] = read_unsized_from_stream(stream, pdf) + pos = stream.tell() + else: + stream.seek(pos, 0) + raise PdfReadError( + "Unable to find 'endstream' marker after stream at byte " + f"{hex(stream.tell())} (nd='{ndstream!r}', end='{end!r}')." + ) + else: + stream.seek(pos, 0) + if "__streamdata__" in data: + return StreamObject.initialize_from_dictionary(data) + else: + retval = DictionaryObject() + retval.update(data) + return retval + + +class TreeObject(DictionaryObject): + def __init__(self, dct: Optional[DictionaryObject] = None) -> None: + DictionaryObject.__init__(self) + if dct: + self.update(dct) + + def has_children(self) -> bool: + return "/First" in self + + def __iter__(self) -> Any: + return self.children() + + def children(self) -> Iterable[Any]: + if not self.has_children(): + return + + child_ref = self[NameObject("/First")] + child = child_ref.get_object() + while True: + yield child + if child == self[NameObject("/Last")]: + return + child_ref = child.get(NameObject("/Next")) # type: ignore + if is_null_or_none(child_ref): + return + child = child_ref.get_object() + + def add_child(self, child: Any, pdf: PdfWriterProtocol) -> None: + self.insert_child(child, None, pdf) + + def inc_parent_counter_default( + self, parent: Union[None, IndirectObject, "TreeObject"], n: int + ) -> None: + if is_null_or_none(parent): + return + assert parent is not None, "mypy" + parent = cast("TreeObject", parent.get_object()) + if "/Count" in parent: + parent[NameObject("/Count")] = NumberObject( + max(0, cast(int, parent[NameObject("/Count")]) + n) + ) + self.inc_parent_counter_default(parent.get("/Parent", None), n) + + def inc_parent_counter_outline( + self, parent: Union[None, IndirectObject, "TreeObject"], n: int + ) -> None: + if is_null_or_none(parent): + return + assert parent is not None, "mypy" + parent = cast("TreeObject", parent.get_object()) + # BooleanObject requires comparison with == not is + opn = parent.get("/%is_open%", True) == True # noqa + c = cast(int, parent.get("/Count", 0)) + if c < 0: + c = abs(c) + parent[NameObject("/Count")] = NumberObject((c + n) * (1 if opn else -1)) + if not opn: + return + self.inc_parent_counter_outline(parent.get("/Parent", None), n) + + def insert_child( + self, + child: Any, + before: Any, + pdf: PdfWriterProtocol, + inc_parent_counter: Optional[Callable[..., Any]] = None, + ) -> IndirectObject: + if inc_parent_counter is None: + inc_parent_counter = self.inc_parent_counter_default + child_obj = child.get_object() + child = child.indirect_reference # get_reference(child_obj) + + prev: Optional[DictionaryObject] + if "/First" not in self: # no child yet + self[NameObject("/First")] = child + self[NameObject("/Count")] = NumberObject(0) + self[NameObject("/Last")] = child + child_obj[NameObject("/Parent")] = self.indirect_reference + inc_parent_counter(self, child_obj.get("/Count", 1)) + if "/Next" in child_obj: + del child_obj["/Next"] + if "/Prev" in child_obj: + del child_obj["/Prev"] + return child + else: + prev = cast("DictionaryObject", self["/Last"]) + + while prev.indirect_reference != before: + if "/Next" in prev: + prev = cast("TreeObject", prev["/Next"]) + else: # append at the end + prev[NameObject("/Next")] = cast("TreeObject", child) + child_obj[NameObject("/Prev")] = prev.indirect_reference + child_obj[NameObject("/Parent")] = self.indirect_reference + if "/Next" in child_obj: + del child_obj["/Next"] + self[NameObject("/Last")] = child + inc_parent_counter(self, child_obj.get("/Count", 1)) + return child + try: # insert as first or in the middle + assert isinstance(prev["/Prev"], DictionaryObject) + prev["/Prev"][NameObject("/Next")] = child + child_obj[NameObject("/Prev")] = prev["/Prev"] + except Exception: # it means we are inserting in first position + del child_obj["/Next"] + child_obj[NameObject("/Next")] = prev + prev[NameObject("/Prev")] = child + child_obj[NameObject("/Parent")] = self.indirect_reference + inc_parent_counter(self, child_obj.get("/Count", 1)) + return child + + def _remove_node_from_tree( + self, prev: Any, prev_ref: Any, cur: Any, last: Any + ) -> None: + """ + Adjust the pointers of the linked list and tree node count. + + Args: + prev: + prev_ref: + cur: + last: + """ + next_ref = cur.get(NameObject("/Next"), None) + if prev is None: + if next_ref: + # Removing first tree node + next_obj = next_ref.get_object() + del next_obj[NameObject("/Prev")] + self[NameObject("/First")] = next_ref + self[NameObject("/Count")] = NumberObject( + self[NameObject("/Count")] - 1 # type: ignore + ) + + else: + # Removing only tree node + self[NameObject("/Count")] = NumberObject(0) + del self[NameObject("/First")] + if NameObject("/Last") in self: + del self[NameObject("/Last")] + else: + if next_ref: + # Removing middle tree node + next_obj = next_ref.get_object() + next_obj[NameObject("/Prev")] = prev_ref + prev[NameObject("/Next")] = next_ref + else: + # Removing last tree node + assert cur == last + del prev[NameObject("/Next")] + self[NameObject("/Last")] = prev_ref + self[NameObject("/Count")] = NumberObject(self[NameObject("/Count")] - 1) # type: ignore + + def remove_child(self, child: Any) -> None: + child_obj = child.get_object() + child = child_obj.indirect_reference + + if NameObject("/Parent") not in child_obj: + raise ValueError("Removed child does not appear to be a tree item") + elif child_obj[NameObject("/Parent")] != self: + raise ValueError("Removed child is not a member of this tree") + + found = False + prev_ref = None + prev = None + cur_ref: Optional[Any] = self[NameObject("/First")] + cur: Optional[Dict[str, Any]] = cur_ref.get_object() # type: ignore + last_ref = self[NameObject("/Last")] + last = last_ref.get_object() + while cur is not None: + if cur == child_obj: + self._remove_node_from_tree(prev, prev_ref, cur, last) + found = True + break + + # Go to the next node + prev_ref = cur_ref + prev = cur + if NameObject("/Next") in cur: + cur_ref = cur[NameObject("/Next")] + cur = cur_ref.get_object() + else: + cur_ref = None + cur = None + + if not found: + raise ValueError("Removal couldn't find item in tree") + + _reset_node_tree_relationship(child_obj) + + def remove_from_tree(self) -> None: + """Remove the object from the tree it is in.""" + if NameObject("/Parent") not in self: + raise ValueError("Removed child does not appear to be a tree item") + else: + cast("TreeObject", self["/Parent"]).remove_child(self) + + def empty_tree(self) -> None: + for child in self: + child_obj = child.get_object() + _reset_node_tree_relationship(child_obj) + + if NameObject("/Count") in self: + del self[NameObject("/Count")] + if NameObject("/First") in self: + del self[NameObject("/First")] + if NameObject("/Last") in self: + del self[NameObject("/Last")] + + +def _reset_node_tree_relationship(child_obj: Any) -> None: + """ + Call this after a node has been removed from a tree. + + This resets the nodes attributes in respect to that tree. + + Args: + child_obj: + """ + del child_obj[NameObject("/Parent")] + if NameObject("/Next") in child_obj: + del child_obj[NameObject("/Next")] + if NameObject("/Prev") in child_obj: + del child_obj[NameObject("/Prev")] + + +class StreamObject(DictionaryObject): + def __init__(self) -> None: + self._data: bytes = b"" + self.decoded_self: Optional[DecodedStreamObject] = None + + def _clone( + self, + src: DictionaryObject, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool, + ignore_fields: Optional[Sequence[Union[str, int]]], + visited: Set[Tuple[int, int]], + ) -> None: + """ + Update the object from src. + + Args: + src: + pdf_dest: + force_duplicate: + ignore_fields: + """ + self._data = cast("StreamObject", src)._data + try: + decoded_self = cast("StreamObject", src).decoded_self + if decoded_self is None: + self.decoded_self = None + else: + self.decoded_self = cast( + "DecodedStreamObject", + decoded_self.clone(pdf_dest, force_duplicate, ignore_fields), + ) + except Exception: + pass + super()._clone(src, pdf_dest, force_duplicate, ignore_fields, visited) + + def hash_bin(self) -> int: + """ + Used to detect modified object. + + Returns: + Hash considering type and value. + """ + # use of _data to prevent errors on non decoded stream such as JBIG2 + return hash((super().hash_bin(), self._data)) + + def get_data(self) -> bytes: + return self._data + + def set_data(self, data: bytes) -> None: + self._data = data + + def hash_value_data(self) -> bytes: + data = super().hash_value_data() + data += self._data + return data + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecation_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + self[NameObject(SA.LENGTH)] = NumberObject(len(self._data)) + DictionaryObject.write_to_stream(self, stream) + del self[SA.LENGTH] + stream.write(b"\nstream\n") + stream.write(self._data) + stream.write(b"\nendstream") + + @staticmethod + def initializeFromDictionary(data: Dict[str, Any]) -> None: + deprecation_with_replacement( + "initializeFromDictionary", "initialize_from_dictionary", "5.0.0" + ) # pragma: no cover + + @staticmethod + def initialize_from_dictionary( + data: Dict[str, Any] + ) -> Union["EncodedStreamObject", "DecodedStreamObject"]: + retval: Union[EncodedStreamObject, DecodedStreamObject] + if SA.FILTER in data: + retval = EncodedStreamObject() + else: + retval = DecodedStreamObject() + retval._data = data["__streamdata__"] + del data["__streamdata__"] + if SA.LENGTH in data: + del data[SA.LENGTH] + retval.update(data) + return retval + + def flate_encode(self, level: int = -1) -> "EncodedStreamObject": + from ..filters import FlateDecode + + if SA.FILTER in self: + f = self[SA.FILTER] + if isinstance(f, ArrayObject): + f = ArrayObject([NameObject(FT.FLATE_DECODE), *f]) + try: + params = ArrayObject( + [NullObject(), *self.get(SA.DECODE_PARMS, ArrayObject())] + ) + except TypeError: + # case of error where the * operator is not working (not an array + params = ArrayObject( + [NullObject(), self.get(SA.DECODE_PARMS, ArrayObject())] + ) + else: + f = ArrayObject([NameObject(FT.FLATE_DECODE), f]) + params = ArrayObject( + [NullObject(), self.get(SA.DECODE_PARMS, NullObject())] + ) + else: + f = NameObject(FT.FLATE_DECODE) + params = None + retval = EncodedStreamObject() + retval.update(self) + retval[NameObject(SA.FILTER)] = f + if params is not None: + retval[NameObject(SA.DECODE_PARMS)] = params + retval._data = FlateDecode.encode(self._data, level) + return retval + + def decode_as_image(self) -> Any: + """ + Try to decode the stream object as an image + + Returns: + a PIL image if proper decoding has been found + Raises: + Exception: (any)during decoding to to invalid object or + errors during decoding will be reported + It is recommended to catch exceptions to prevent + stops in your program. + """ + from ..filters import _xobj_to_image + + if self.get("/Subtype", "") != "/Image": + try: + msg = f"{self.indirect_reference} does not seem to be an Image" # pragma: no cover + except AttributeError: + msg = f"{self.__repr__()} object does not seem to be an Image" # pragma: no cover + logger_warning(msg, __name__) + extension, byte_stream, img = _xobj_to_image(self) + if extension is None: + return None # pragma: no cover + return img + + +class DecodedStreamObject(StreamObject): + pass + + +class EncodedStreamObject(StreamObject): + def __init__(self) -> None: + self.decoded_self: Optional[DecodedStreamObject] = None + + # This overrides the parent method: + def get_data(self) -> bytes: + from ..filters import decode_stream_data + + if self.decoded_self is not None: + # cached version of decoded object + return self.decoded_self.get_data() + else: + # create decoded object + decoded = DecodedStreamObject() + + decoded.set_data(decode_stream_data(self)) + for key, value in list(self.items()): + if key not in (SA.LENGTH, SA.FILTER, SA.DECODE_PARMS): + decoded[key] = value + self.decoded_self = decoded + return decoded.get_data() + + # This overrides the parent method: + def set_data(self, data: bytes) -> None: + from ..filters import FlateDecode + + if self.get(SA.FILTER, "") in (FT.FLATE_DECODE, [FT.FLATE_DECODE]): + if not isinstance(data, bytes): + raise TypeError("data must be bytes") + if self.decoded_self is None: + self.get_data() # to create self.decoded_self + assert self.decoded_self is not None, "mypy" + self.decoded_self.set_data(data) + super().set_data(FlateDecode.encode(data)) + else: + raise PdfReadError( + "Streams encoded with a filter different from FlateDecode are not supported" + ) + + +class ContentStream(DecodedStreamObject): + """ + In order to be fast, this data structure can contain either: + + * raw data in ._data + * parsed stream operations in ._operations. + + At any time, ContentStream object can either have both of those fields defined, + or one field defined and the other set to None. + + These fields are "rebuilt" lazily, when accessed: + + * when .get_data() is called, if ._data is None, it is rebuilt from ._operations. + * when .operations is called, if ._operations is None, it is rebuilt from ._data. + + Conversely, these fields can be invalidated: + + * when .set_data() is called, ._operations is set to None. + * when .operations is set, ._data is set to None. + """ + + def __init__( + self, + stream: Any, + pdf: Any, + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, + ) -> None: + self.pdf = pdf + + # The inner list has two elements: + # Element 0: List + # Element 1: str + self._operations: List[Tuple[Any, bytes]] = [] + + # stream may be a StreamObject or an ArrayObject containing + # multiple StreamObjects to be cat'd together. + if stream is None: + super().set_data(b"") + else: + stream = stream.get_object() + if isinstance(stream, ArrayObject): + data = b"" + for s in stream: + data += s.get_object().get_data() + if len(data) == 0 or data[-1] != b"\n": + data += b"\n" + super().set_data(bytes(data)) + else: + stream_data = stream.get_data() + assert stream_data is not None + super().set_data(stream_data) + self.forced_encoding = forced_encoding + + def clone( + self, + pdf_dest: Any, + force_duplicate: bool = False, + ignore_fields: Optional[Sequence[Union[str, int]]] = (), + ) -> "ContentStream": + """ + Clone object into pdf_dest. + + Args: + pdf_dest: + force_duplicate: + ignore_fields: + + Returns: + The cloned ContentStream + """ + try: + if self.indirect_reference.pdf == pdf_dest and not force_duplicate: # type: ignore + return self + except Exception: + pass + + visited: Set[Tuple[int, int]] = set() + d__ = cast( + "ContentStream", + self._reference_clone( + self.__class__(None, None), pdf_dest, force_duplicate + ), + ) + if ignore_fields is None: + ignore_fields = [] + d__._clone(self, pdf_dest, force_duplicate, ignore_fields, visited) + return d__ + + def _clone( + self, + src: DictionaryObject, + pdf_dest: PdfWriterProtocol, + force_duplicate: bool, + ignore_fields: Optional[Sequence[Union[str, int]]], + visited: Set[Tuple[int, int]], + ) -> None: + """ + Update the object from src. + + Args: + src: + pdf_dest: + force_duplicate: + ignore_fields: + """ + src_cs = cast("ContentStream", src) + super().set_data(src_cs._data) + self.pdf = pdf_dest + self._operations = list(src_cs._operations) + self.forced_encoding = src_cs.forced_encoding + # no need to call DictionaryObjection or anything + # like super(DictionaryObject,self)._clone(src, pdf_dest, force_duplicate, ignore_fields, visited) + + def _parse_content_stream(self, stream: StreamType) -> None: + # 7.8.2 Content Streams + stream.seek(0, 0) + operands: List[Union[int, str, PdfObject]] = [] + while True: + peek = read_non_whitespace(stream) + if peek == b"" or peek == 0: + break + stream.seek(-1, 1) + if peek.isalpha() or peek in (b"'", b'"'): + operator = read_until_regex(stream, NameObject.delimiter_pattern) + if operator == b"BI": + # begin inline image - a completely different parsing + # mechanism is required, of course... thanks buddy... + assert operands == [] + ii = self._read_inline_image(stream) + self._operations.append((ii, b"INLINE IMAGE")) + else: + self._operations.append((operands, operator)) + operands = [] + elif peek == b"%": + # If we encounter a comment in the content stream, we have to + # handle it here. Typically, read_object will handle + # encountering a comment -- but read_object assumes that + # following the comment must be the object we're trying to + # read. In this case, it could be an operator instead. + while peek not in (b"\r", b"\n", b""): + peek = stream.read(1) + else: + operands.append(read_object(stream, None, self.forced_encoding)) + + def _read_inline_image(self, stream: StreamType) -> Dict[str, Any]: + # begin reading just after the "BI" - begin image + # first read the dictionary of settings. + settings = DictionaryObject() + while True: + tok = read_non_whitespace(stream) + stream.seek(-1, 1) + if tok == b"I": + # "ID" - begin of image data + break + key = read_object(stream, self.pdf) + tok = read_non_whitespace(stream) + stream.seek(-1, 1) + value = read_object(stream, self.pdf) + settings[key] = value + # left at beginning of ID + tmp = stream.read(3) + assert tmp[:2] == b"ID" + filtr = settings.get("/F", settings.get("/Filter", "not set")) + savpos = stream.tell() + if isinstance(filtr, list): + filtr = filtr[0] # used forencoding + if "AHx" in filtr or "ASCIIHexDecode" in filtr: + data = extract_inline_AHx(stream) + elif "A85" in filtr or "ASCII85Decode" in filtr: + data = extract_inline_A85(stream) + elif "RL" in filtr or "RunLengthDecode" in filtr: + data = extract_inline_RL(stream) + elif "DCT" in filtr or "DCTDecode" in filtr: + data = extract_inline_DCT(stream) + elif filtr == "not set": + cs = settings.get("/CS", "") + if "RGB" in cs: + lcs = 3 + elif "CMYK" in cs: + lcs = 4 + else: + bits = settings.get( + "/BPC", + 8 if cs in {"/I", "/G", "/Indexed", "/DeviceGray"} else -1, + ) + if bits > 0: + lcs = bits / 8.0 + else: + data = extract_inline_default(stream) + lcs = -1 + if lcs > 0: + data = stream.read( + ceil(cast(int, settings["/W"]) * lcs) * cast(int, settings["/H"]) + ) + ei = read_non_whitespace(stream) + stream.seek(-1, 1) + else: + data = extract_inline_default(stream) + + ei = stream.read(3) + stream.seek(-1, 1) + if ei[0:2] != b"EI" or ei[2:3] not in WHITESPACES: + stream.seek(savpos, 0) + data = extract_inline_default(stream) + return {"settings": settings, "data": data} + + # This overrides the parent method: + def get_data(self) -> bytes: + if not self._data: + new_data = BytesIO() + for operands, operator in self._operations: + if operator == b"INLINE IMAGE": + new_data.write(b"BI") + dict_text = BytesIO() + operands["settings"].write_to_stream(dict_text) + new_data.write(dict_text.getvalue()[2:-2]) + new_data.write(b"ID ") + new_data.write(operands["data"]) + new_data.write(b"EI") + else: + for op in operands: + op.write_to_stream(new_data) + new_data.write(b" ") + new_data.write(operator) + new_data.write(b"\n") + self._data = new_data.getvalue() + return self._data + + # This overrides the parent method: + def set_data(self, data: bytes) -> None: + super().set_data(data) + self._operations = [] + + @property + def operations(self) -> List[Tuple[Any, Any]]: + if not self._operations and self._data: + self._parse_content_stream(BytesIO(self._data)) + self._data = b"" + return self._operations + + @operations.setter + def operations(self, operations: List[Tuple[Any, bytes]]) -> None: + self._operations = operations + self._data = b"" + + def isolate_graphics_state(self) -> None: + if self._operations: + self._operations.insert(0, ([], b"q")) + self._operations.append(([], b"Q")) + elif self._data: + self._data = b"q\n" + self._data + b"\nQ\n" + + # This overrides the parent method: + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if not self._data and self._operations: + self.get_data() # this ensures ._data is rebuilt + super().write_to_stream(stream, encryption_key) + + +def read_object( + stream: StreamType, + pdf: Optional[PdfReaderProtocol], + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, +) -> Union[PdfObject, int, str, ContentStream]: + tok = stream.read(1) + stream.seek(-1, 1) # reset to start + if tok == b"/": + return NameObject.read_from_stream(stream, pdf) + elif tok == b"<": + # hexadecimal string OR dictionary + peek = stream.read(2) + stream.seek(-2, 1) # reset to start + if peek == b"<<": + return DictionaryObject.read_from_stream(stream, pdf, forced_encoding) + else: + return read_hex_string_from_stream(stream, forced_encoding) + elif tok == b"[": + return ArrayObject.read_from_stream(stream, pdf, forced_encoding) + elif tok == b"t" or tok == b"f": + return BooleanObject.read_from_stream(stream) + elif tok == b"(": + return read_string_from_stream(stream, forced_encoding) + elif tok == b"e" and stream.read(6) == b"endobj": + stream.seek(-6, 1) + return NullObject() + elif tok == b"n": + return NullObject.read_from_stream(stream) + elif tok == b"%": + # comment + while tok not in (b"\r", b"\n"): + tok = stream.read(1) + # Prevents an infinite loop by raising an error if the stream is at + # the EOF + if len(tok) <= 0: + raise PdfStreamError("File ended unexpectedly.") + tok = read_non_whitespace(stream) + stream.seek(-1, 1) + return read_object(stream, pdf, forced_encoding) + elif tok in b"0123456789+-.": + # number object OR indirect reference + peek = stream.read(20) + stream.seek(-len(peek), 1) # reset to start + if IndirectPattern.match(peek) is not None: + assert pdf is not None # hint for mypy + return IndirectObject.read_from_stream(stream, pdf) + else: + return NumberObject.read_from_stream(stream) + else: + stream.seek(-20, 1) + raise PdfReadError( + f"Invalid Elementary Object starting with {tok!r} @{stream.tell()}: {stream.read(80).__repr__()}" + ) + + +class Field(TreeObject): + """ + A class representing a field dictionary. + + This class is accessed through + :meth:`get_fields()` + """ + + def __init__(self, data: DictionaryObject) -> None: + DictionaryObject.__init__(self) + field_attributes = ( + FieldDictionaryAttributes.attributes() + + CheckboxRadioButtonAttributes.attributes() + ) + self.indirect_reference = data.indirect_reference + for attr in field_attributes: + try: + self[NameObject(attr)] = data[attr] + except KeyError: + pass + if isinstance(self.get("/V"), EncodedStreamObject): + d = cast(EncodedStreamObject, self[NameObject("/V")]).get_data() + if isinstance(d, bytes): + d_str = d.decode() + elif d is None: + d_str = "" + else: + raise Exception("Should never happen") + self[NameObject("/V")] = TextStringObject(d_str) + + # TABLE 8.69 Entries common to all field dictionaries + @property + def field_type(self) -> Optional[NameObject]: + """Read-only property accessing the type of this field.""" + return self.get(FieldDictionaryAttributes.FT) + + @property + def parent(self) -> Optional[DictionaryObject]: + """Read-only property accessing the parent of this field.""" + return self.get(FieldDictionaryAttributes.Parent) + + @property + def kids(self) -> Optional["ArrayObject"]: + """Read-only property accessing the kids of this field.""" + return self.get(FieldDictionaryAttributes.Kids) + + @property + def name(self) -> Optional[str]: + """Read-only property accessing the name of this field.""" + return self.get(FieldDictionaryAttributes.T) + + @property + def alternate_name(self) -> Optional[str]: + """Read-only property accessing the alternate name of this field.""" + return self.get(FieldDictionaryAttributes.TU) + + @property + def mapping_name(self) -> Optional[str]: + """ + Read-only property accessing the mapping name of this field. + + This name is used by pypdf as a key in the dictionary returned by + :meth:`get_fields()` + """ + return self.get(FieldDictionaryAttributes.TM) + + @property + def flags(self) -> Optional[int]: + """ + Read-only property accessing the field flags, specifying various + characteristics of the field (see Table 8.70 of the PDF 1.7 reference). + """ + return self.get(FieldDictionaryAttributes.Ff) + + @property + def value(self) -> Optional[Any]: + """ + Read-only property accessing the value of this field. + + Format varies based on field type. + """ + return self.get(FieldDictionaryAttributes.V) + + @property + def default_value(self) -> Optional[Any]: + """Read-only property accessing the default value of this field.""" + return self.get(FieldDictionaryAttributes.DV) + + @property + def additional_actions(self) -> Optional[DictionaryObject]: + """ + Read-only property accessing the additional actions dictionary. + + This dictionary defines the field's behavior in response to trigger + events. See Section 8.5.2 of the PDF 1.7 reference. + """ + return self.get(FieldDictionaryAttributes.AA) + + +class Destination(TreeObject): + """ + A class representing a destination within a PDF file. + + See section 12.3.2 of the PDF 2.0 reference. + + Args: + title: Title of this destination. + page: Reference to the page of this destination. Should + be an instance of :class:`IndirectObject`. + fit: How the destination is displayed. + + Raises: + PdfReadError: If destination type is invalid. + """ + + node: Optional[ + DictionaryObject + ] = None # node provide access to the original Object + + def __init__( + self, + title: str, + page: Union[NumberObject, IndirectObject, NullObject, DictionaryObject], + fit: Fit, + ) -> None: + self._filtered_children: List[Any] = [] # used in PdfWriter + + typ = fit.fit_type + args = fit.fit_args + + DictionaryObject.__init__(self) + self[NameObject("/Title")] = TextStringObject(title) + self[NameObject("/Page")] = page + self[NameObject("/Type")] = typ + + # from table 8.2 of the PDF 1.7 reference. + if typ == "/XYZ": + if len(args) < 1: # left is missing : should never occur + args.append(NumberObject(0.0)) + if len(args) < 2: # top is missing + args.append(NumberObject(0.0)) + if len(args) < 3: # zoom is missing + args.append(NumberObject(0.0)) + ( + self[NameObject(TA.LEFT)], + self[NameObject(TA.TOP)], + self[NameObject("/Zoom")], + ) = args + elif len(args) == 0: + pass + elif typ == TF.FIT_R: + ( + self[NameObject(TA.LEFT)], + self[NameObject(TA.BOTTOM)], + self[NameObject(TA.RIGHT)], + self[NameObject(TA.TOP)], + ) = args + elif typ in [TF.FIT_H, TF.FIT_BH]: + try: # Preferred to be more robust not only to null parameters + (self[NameObject(TA.TOP)],) = args + except Exception: + (self[NameObject(TA.TOP)],) = (NullObject(),) + elif typ in [TF.FIT_V, TF.FIT_BV]: + try: # Preferred to be more robust not only to null parameters + (self[NameObject(TA.LEFT)],) = args + except Exception: + (self[NameObject(TA.LEFT)],) = (NullObject(),) + elif typ in [TF.FIT, TF.FIT_B]: + pass + else: + raise PdfReadError(f"Unknown Destination Type: {typ!r}") + + @property + def dest_array(self) -> "ArrayObject": + return ArrayObject( + [self.raw_get("/Page"), self["/Type"]] + + [ + self[x] + for x in ["/Left", "/Bottom", "/Right", "/Top", "/Zoom"] + if x in self + ] + ) + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecation_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(b"<<\n") + key = NameObject("/D") + key.write_to_stream(stream) + stream.write(b" ") + value = self.dest_array + value.write_to_stream(stream) + + key = NameObject("/S") + key.write_to_stream(stream) + stream.write(b" ") + value_s = NameObject("/GoTo") + value_s.write_to_stream(stream) + + stream.write(b"\n") + stream.write(b">>") + + @property + def title(self) -> Optional[str]: + """Read-only property accessing the destination title.""" + return self.get("/Title") + + @property + def page(self) -> Optional[int]: + """Read-only property accessing the destination page number.""" + return self.get("/Page") + + @property + def typ(self) -> Optional[str]: + """Read-only property accessing the destination type.""" + return self.get("/Type") + + @property + def zoom(self) -> Optional[int]: + """Read-only property accessing the zoom factor.""" + return self.get("/Zoom", None) + + @property + def left(self) -> Optional[FloatObject]: + """Read-only property accessing the left horizontal coordinate.""" + return self.get("/Left", None) + + @property + def right(self) -> Optional[FloatObject]: + """Read-only property accessing the right horizontal coordinate.""" + return self.get("/Right", None) + + @property + def top(self) -> Optional[FloatObject]: + """Read-only property accessing the top vertical coordinate.""" + return self.get("/Top", None) + + @property + def bottom(self) -> Optional[FloatObject]: + """Read-only property accessing the bottom vertical coordinate.""" + return self.get("/Bottom", None) + + @property + def color(self) -> Optional["ArrayObject"]: + """Read-only property accessing the color in (R, G, B) with values 0.0-1.0.""" + return self.get( + "/C", ArrayObject([FloatObject(0), FloatObject(0), FloatObject(0)]) + ) + + @property + def font_format(self) -> Optional[OutlineFontFlag]: + """ + Read-only property accessing the font type. + + 1=italic, 2=bold, 3=both + """ + return self.get("/F", 0) + + @property + def outline_count(self) -> Optional[int]: + """ + Read-only property accessing the outline count. + + positive = expanded + negative = collapsed + absolute value = number of visible descendants at all levels + """ + return self.get("/Count", None) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_fit.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_fit.py new file mode 100644 index 00000000..c44d12b4 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_fit.py @@ -0,0 +1,169 @@ +from typing import Any, Optional, Tuple, Union + +from ._base import is_null_or_none + + +class Fit: + def __init__( + self, fit_type: str, fit_args: Tuple[Union[None, float, Any], ...] = () + ): + from ._base import FloatObject, NameObject, NullObject + + self.fit_type = NameObject(fit_type) + self.fit_args = [ + NullObject() if is_null_or_none(a) else FloatObject(a) for a in fit_args + ] + + @classmethod + def xyz( + cls, + left: Optional[float] = None, + top: Optional[float] = None, + zoom: Optional[float] = None, + ) -> "Fit": + """ + Display the page designated by page, with the coordinates (left, top) + positioned at the upper-left corner of the window and the contents + of the page magnified by the factor zoom. + + A null value for any of the parameters left, top, or zoom specifies + that the current value of that parameter is to be retained unchanged. + + A zoom value of 0 has the same meaning as a null value. + + Args: + left: + top: + zoom: + + Returns: + The created fit object. + """ + return Fit(fit_type="/XYZ", fit_args=(left, top, zoom)) + + @classmethod + def fit(cls) -> "Fit": + """ + Display the page designated by page, with its contents magnified just + enough to fit the entire page within the window both horizontally and + vertically. + + If the required horizontal and vertical magnification factors are + different, use the smaller of the two, centering the page within the + window in the other dimension. + """ + return Fit(fit_type="/Fit") + + @classmethod + def fit_horizontally(cls, top: Optional[float] = None) -> "Fit": + """ + Display the page designated by page, with the vertical coordinate top + positioned at the top edge of the window and the contents of the page + magnified just enough to fit the entire width of the page within the + window. + + A null value for ``top`` specifies that the current value of that + parameter is to be retained unchanged. + + Args: + top: + + Returns: + The created fit object. + """ + return Fit(fit_type="/FitH", fit_args=(top,)) + + @classmethod + def fit_vertically(cls, left: Optional[float] = None) -> "Fit": + return Fit(fit_type="/FitV", fit_args=(left,)) + + @classmethod + def fit_rectangle( + cls, + left: Optional[float] = None, + bottom: Optional[float] = None, + right: Optional[float] = None, + top: Optional[float] = None, + ) -> "Fit": + """ + Display the page designated by page, with its contents magnified + just enough to fit the rectangle specified by the coordinates + left, bottom, right, and top entirely within the window + both horizontally and vertically. + + If the required horizontal and vertical magnification factors are + different, use the smaller of the two, centering the rectangle within + the window in the other dimension. + + A null value for any of the parameters may result in unpredictable + behavior. + + Args: + left: + bottom: + right: + top: + + Returns: + The created fit object. + """ + return Fit(fit_type="/FitR", fit_args=(left, bottom, right, top)) + + @classmethod + def fit_box(cls) -> "Fit": + """ + Display the page designated by page, with its contents magnified just + enough to fit its bounding box entirely within the window both + horizontally and vertically. + + If the required horizontal and vertical magnification factors are + different, use the smaller of the two, centering the bounding box + within the window in the other dimension. + """ + return Fit(fit_type="/FitB") + + @classmethod + def fit_box_horizontally(cls, top: Optional[float] = None) -> "Fit": + """ + Display the page designated by page, with the vertical coordinate top + positioned at the top edge of the window and the contents of the page + magnified just enough to fit the entire width of its bounding box + within the window. + + A null value for top specifies that the current value of that parameter + is to be retained unchanged. + + Args: + top: + + Returns: + The created fit object. + """ + return Fit(fit_type="/FitBH", fit_args=(top,)) + + @classmethod + def fit_box_vertically(cls, left: Optional[float] = None) -> "Fit": + """ + Display the page designated by page, with the horizontal coordinate + left positioned at the left edge of the window and the contents of the + page magnified just enough to fit the entire height of its bounding box + within the window. + + A null value for left specifies that the current value of that + parameter is to be retained unchanged. + + Args: + left: + + Returns: + The created fit object. + """ + return Fit(fit_type="/FitBV", fit_args=(left,)) + + def __str__(self) -> str: + if not self.fit_args: + return f"Fit({self.fit_type})" + return f"Fit({self.fit_type}, {self.fit_args})" + + +DEFAULT_FIT = Fit.fit() diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_image_inline.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_image_inline.py new file mode 100644 index 00000000..41826ac3 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_image_inline.py @@ -0,0 +1,235 @@ +# Copyright (c) 2024, pypdf contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import logging +from io import BytesIO + +from .._utils import ( + WHITESPACES, + StreamType, + read_non_whitespace, +) +from ..errors import PdfReadError + +logger = logging.getLogger(__name__) + +BUFFER_SIZE = 8192 + + +def extract_inline_AHx(stream: StreamType) -> bytes: + """ + Extract HexEncoded Stream from Inline Image. + the stream will be moved onto the EI + """ + data_out: bytes = b"" + # Read data until delimiter > and EI as backup + # ignoring backup. + while True: + data_buffered = read_non_whitespace(stream) + stream.read(BUFFER_SIZE) + if not data_buffered: + raise PdfReadError("Unexpected end of stream") + pos_tok = data_buffered.find(b">") + if pos_tok >= 0: # found > + data_out += data_buffered[: (pos_tok + 1)] + stream.seek(-len(data_buffered) + pos_tok + 1, 1) + break + pos_ei = data_buffered.find(b"EI") + if pos_ei >= 0: # found EI + stream.seek(-len(data_buffered) + pos_ei - 1, 1) + c = stream.read(1) + while c in WHITESPACES: + stream.seek(-2, 1) + c = stream.read(1) + pos_ei -= 1 + data_out += data_buffered[:pos_ei] + break + elif len(data_buffered) == 2: + data_out += data_buffered + raise PdfReadError("Unexpected end of stream") + else: # > nor EI found + data_out += data_buffered[:-2] + stream.seek(-2, 1) + + ei_tok = read_non_whitespace(stream) + ei_tok += stream.read(2) + stream.seek(-3, 1) + if ei_tok[0:2] != b"EI" or not (ei_tok[2:3] == b"" or ei_tok[2:3] in WHITESPACES): + raise PdfReadError("EI stream not found") + return data_out + + +def extract_inline_A85(stream: StreamType) -> bytes: + """ + Extract A85 Stream from Inline Image. + the stream will be moved onto the EI + """ + data_out: bytes = b"" + # Read data up to delimiter ~> + # see §3.3.2 from PDF ref 1.7 + while True: + data_buffered = read_non_whitespace(stream) + stream.read(BUFFER_SIZE) + if not data_buffered: + raise PdfReadError("Unexpected end of stream") + pos_tok = data_buffered.find(b"~>") + if pos_tok >= 0: # found! + data_out += data_buffered[: pos_tok + 2] + stream.seek(-len(data_buffered) + pos_tok + 2, 1) + break + elif len(data_buffered) == 2: # end of buffer + data_out += data_buffered + raise PdfReadError("Unexpected end of stream") + data_out += data_buffered[ + :-2 + ] # back by one char in case of in the middle of ~> + stream.seek(-2, 1) + + ei_tok = read_non_whitespace(stream) + ei_tok += stream.read(2) + stream.seek(-3, 1) + if ei_tok[0:2] != b"EI" or not (ei_tok[2:3] == b"" or ei_tok[2:3] in WHITESPACES): + raise PdfReadError("EI stream not found") + return data_out + + +def extract_inline_RL(stream: StreamType) -> bytes: + """ + Extract RL Stream from Inline Image. + the stream will be moved onto the EI + """ + data_out: bytes = b"" + # Read data up to delimiter ~> + # see §3.3.4 from PDF ref 1.7 + while True: + data_buffered = stream.read(BUFFER_SIZE) + if not data_buffered: + raise PdfReadError("Unexpected end of stream") + pos_tok = data_buffered.find(b"\x80") + if pos_tok >= 0: # found + data_out += data_buffered[: pos_tok + 1] + stream.seek(-len(data_buffered) + pos_tok + 1, 1) + break + data_out += data_buffered + + ei_tok = read_non_whitespace(stream) + ei_tok += stream.read(2) + stream.seek(-3, 1) + if ei_tok[0:2] != b"EI" or not (ei_tok[2:3] == b"" or ei_tok[2:3] in WHITESPACES): + raise PdfReadError("EI stream not found") + return data_out + + +def extract_inline_DCT(stream: StreamType) -> bytes: + """ + Extract DCT (JPEG) Stream from Inline Image. + the stream will be moved onto the EI + """ + data_out: bytes = b"" + # Read Blocks of data (ID/Size/data) up to ID=FF/D9 + # see https://www.digicamsoft.com/itu/itu-t81-36.html + notfirst = False + while True: + c = stream.read(1) + if notfirst or (c == b"\xff"): + data_out += c + if c != b"\xff": + continue + else: + notfirst = True + c = stream.read(1) + data_out += c + if c == b"\xff": + stream.seek(-1, 1) # pragma: no cover + elif c == b"\x00": # stuffing + pass + elif c == b"\xd9": # end + break + elif c in ( + b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc9\xca\xcb\xcc\xcd\xce\xcf" + b"\xda\xdb\xdc\xdd\xde\xdf" + b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xfe" + ): + c = stream.read(2) + data_out += c + sz = c[0] * 256 + c[1] + data_out += stream.read(sz - 2) + # else: pass + + ei_tok = read_non_whitespace(stream) + ei_tok += stream.read(2) + stream.seek(-3, 1) + if ei_tok[0:2] != b"EI" or not (ei_tok[2:3] == b"" or ei_tok[2:3] in WHITESPACES): + raise PdfReadError("EI stream not found") + return data_out + + +def extract_inline_default(stream: StreamType) -> bytes: + """ + Legacy method + used by default + """ + stream_out = BytesIO() + # Read the inline image, while checking for EI (End Image) operator. + while True: + data_buffered = stream.read(BUFFER_SIZE) + if not data_buffered: + raise PdfReadError("Unexpected end of stream") + pos_ei = data_buffered.find( + b"E" + ) # we can not look straight for "EI" because it may not have been loaded in the buffer + + if pos_ei == -1: + stream_out.write(data_buffered) + else: + # Write out everything including E (the one from EI to be removed). + stream_out.write(data_buffered[0 : pos_ei + 1]) + sav_pos_ei = stream_out.tell() - 1 + # Seek back in the stream to read the E next. + stream.seek(pos_ei + 1 - len(data_buffered), 1) + saved_pos = stream.tell() + # Check for End Image + tok2 = stream.read(1) # I of "EI" + if tok2 != b"I": + stream.seek(saved_pos, 0) + continue + tok3 = stream.read(1) # possible space after "EI" + if tok3 not in WHITESPACES: + stream.seek(saved_pos, 0) + continue + while tok3 in WHITESPACES: + tok3 = stream.read(1) + if data_buffered[pos_ei - 1 : pos_ei] not in WHITESPACES and tok3 not in { + b"Q", + b"E", + }: # for Q ou EMC + stream.seek(saved_pos, 0) + continue + # Data contains [\s]EI[\s](Q|EMC): 4 chars are sufficients + # remove E(I) wrongly inserted earlier + stream_out.truncate(sav_pos_ei) + break + + return stream_out.getvalue() diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_outline.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_outline.py new file mode 100644 index 00000000..4d6a47da --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_outline.py @@ -0,0 +1,33 @@ +from typing import Union + +from .._utils import StreamType, deprecate_no_replacement +from ._base import NameObject +from ._data_structures import Destination + + +class OutlineItem(Destination): + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + stream.write(b"<<\n") + for key in [ + NameObject(x) + for x in ["/Title", "/Parent", "/First", "/Last", "/Next", "/Prev"] + if x in self + ]: + key.write_to_stream(stream) + stream.write(b" ") + value = self.raw_get(key) + value.write_to_stream(stream) + stream.write(b"\n") + key = NameObject("/Dest") + key.write_to_stream(stream) + stream.write(b" ") + value = self.dest_array + value.write_to_stream(stream) + stream.write(b"\n") + stream.write(b">>") diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_rectangle.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_rectangle.py new file mode 100644 index 00000000..c1f22ceb --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_rectangle.py @@ -0,0 +1,132 @@ +from typing import Any, Tuple, Union + +from ._base import FloatObject, NumberObject +from ._data_structures import ArrayObject + + +class RectangleObject(ArrayObject): + """ + This class is used to represent *page boxes* in pypdf. + + These boxes include: + + * :attr:`artbox ` + * :attr:`bleedbox ` + * :attr:`cropbox ` + * :attr:`mediabox ` + * :attr:`trimbox ` + """ + + def __init__( + self, arr: Union["RectangleObject", Tuple[float, float, float, float]] + ) -> None: + # must have four points + assert len(arr) == 4 + # automatically convert arr[x] into NumberObject(arr[x]) if necessary + ArrayObject.__init__(self, [self._ensure_is_number(x) for x in arr]) # type: ignore + + def _ensure_is_number(self, value: Any) -> Union[FloatObject, NumberObject]: + if not isinstance(value, (FloatObject, NumberObject)): + value = FloatObject(value) + return value + + def scale(self, sx: float, sy: float) -> "RectangleObject": + return RectangleObject( + ( + float(self.left) * sx, + float(self.bottom) * sy, + float(self.right) * sx, + float(self.top) * sy, + ) + ) + + def __repr__(self) -> str: + return f"RectangleObject({list(self)!r})" + + @property + def left(self) -> FloatObject: + return self[0] + + @left.setter + def left(self, f: float) -> None: + self[0] = FloatObject(f) + + @property + def bottom(self) -> FloatObject: + return self[1] + + @bottom.setter + def bottom(self, f: float) -> None: + self[1] = FloatObject(f) + + @property + def right(self) -> FloatObject: + return self[2] + + @right.setter + def right(self, f: float) -> None: + self[2] = FloatObject(f) + + @property + def top(self) -> FloatObject: + return self[3] + + @top.setter + def top(self, f: float) -> None: + self[3] = FloatObject(f) + + @property + def lower_left(self) -> Tuple[float, float]: + """ + Property to read and modify the lower left coordinate of this box + in (x,y) form. + """ + return self.left, self.bottom + + @lower_left.setter + def lower_left(self, value: Tuple[float, float]) -> None: + self[0], self[1] = (self._ensure_is_number(x) for x in value) + + @property + def lower_right(self) -> Tuple[float, float]: + """ + Property to read and modify the lower right coordinate of this box + in (x,y) form. + """ + return self.right, self.bottom + + @lower_right.setter + def lower_right(self, value: Tuple[float, float]) -> None: + self[2], self[1] = (self._ensure_is_number(x) for x in value) + + @property + def upper_left(self) -> Tuple[float, float]: + """ + Property to read and modify the upper left coordinate of this box + in (x,y) form. + """ + return self.left, self.top + + @upper_left.setter + def upper_left(self, value: Tuple[float, float]) -> None: + self[0], self[3] = (self._ensure_is_number(x) for x in value) + + @property + def upper_right(self) -> Tuple[float, float]: + """ + Property to read and modify the upper right coordinate of this box + in (x,y) form. + """ + return self.right, self.top + + @upper_right.setter + def upper_right(self, value: Tuple[float, float]) -> None: + self[2], self[3] = (self._ensure_is_number(x) for x in value) + + @property + def width(self) -> float: + return self.right - self.left + + @property + def height(self) -> float: + return self.top - self.bottom diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_utils.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_utils.py new file mode 100644 index 00000000..6fce6d0b --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_utils.py @@ -0,0 +1,209 @@ +import codecs +from typing import Dict, List, Tuple, Union + +from .._codecs import _pdfdoc_encoding +from .._utils import StreamType, logger_warning, read_non_whitespace +from ..errors import STREAM_TRUNCATED_PREMATURELY, PdfStreamError +from ._base import ByteStringObject, TextStringObject + + +def hex_to_rgb(value: str) -> Tuple[float, float, float]: + return tuple(int(value.lstrip("#")[i : i + 2], 16) / 255.0 for i in (0, 2, 4)) # type: ignore + + +def read_hex_string_from_stream( + stream: StreamType, + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, +) -> Union["TextStringObject", "ByteStringObject"]: + stream.read(1) + arr = [] + x = b"" + while True: + tok = read_non_whitespace(stream) + if not tok: + raise PdfStreamError(STREAM_TRUNCATED_PREMATURELY) + if tok == b">": + break + x += tok + if len(x) == 2: + arr.append(int(x, base=16)) + x = b"" + if len(x) == 1: + x += b"0" + if x != b"": + arr.append(int(x, base=16)) + return create_string_object(bytes(arr), forced_encoding) + + +__ESPACE_DICT__ = { + b"n": ord(b"\n"), + b"r": ord(b"\r"), + b"t": ord(b"\t"), + b"b": ord(b"\b"), + b"f": ord(b"\f"), + b"(": ord(b"("), + b")": ord(b")"), + b"/": ord(b"/"), + b"\\": ord(b"\\"), + b" ": ord(b" "), + b"%": ord(b"%"), + b"<": ord(b"<"), + b">": ord(b">"), + b"[": ord(b"["), + b"]": ord(b"]"), + b"#": ord(b"#"), + b"_": ord(b"_"), + b"&": ord(b"&"), + b"$": ord(b"$"), +} +__BACKSLASH_CODE__ = 92 + + +def read_string_from_stream( + stream: StreamType, + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, +) -> Union["TextStringObject", "ByteStringObject"]: + tok = stream.read(1) + parens = 1 + txt = [] + while True: + tok = stream.read(1) + if not tok: + raise PdfStreamError(STREAM_TRUNCATED_PREMATURELY) + if tok == b"(": + parens += 1 + elif tok == b")": + parens -= 1 + if parens == 0: + break + elif tok == b"\\": + tok = stream.read(1) + try: + txt.append(__ESPACE_DICT__[tok]) + continue + except KeyError: + if b"0" <= tok <= b"7": + # "The number ddd may consist of one, two, or three + # octal digits; high-order overflow shall be ignored. + # Three octal digits shall be used, with leading zeros + # as needed, if the next character of the string is also + # a digit." (PDF reference 7.3.4.2, p 16) + sav = stream.tell() - 1 + for _ in range(2): + ntok = stream.read(1) + if b"0" <= ntok <= b"7": + tok += ntok + else: + stream.seek(-1, 1) # ntok has to be analyzed + break + i = int(tok, base=8) + if i > 255: + txt.append(__BACKSLASH_CODE__) + stream.seek(sav) + else: + txt.append(i) + continue + elif tok in b"\n\r": + # This case is hit when a backslash followed by a line + # break occurs. If it's a multi-char EOL, consume the + # second character: + tok = stream.read(1) + if tok not in b"\n\r": + stream.seek(-1, 1) + # Then don't add anything to the actual string, since this + # line break was escaped: + continue + else: + msg = f"Unexpected escaped string: {tok.decode('utf-8','ignore')}" + logger_warning(msg, __name__) + txt.append(__BACKSLASH_CODE__) + txt.append(ord(tok)) + return create_string_object(bytes(txt), forced_encoding) + + +def create_string_object( + string: Union[str, bytes], + forced_encoding: Union[None, str, List[str], Dict[int, str]] = None, +) -> Union[TextStringObject, ByteStringObject]: + """ + Create a ByteStringObject or a TextStringObject from a string to represent the string. + + Args: + string: The data being used + forced_encoding: Typically None, or an encoding string + + Returns: + A ByteStringObject + + Raises: + TypeError: If string is not of type str or bytes. + """ + if isinstance(string, str): + return TextStringObject(string) + elif isinstance(string, bytes): + if isinstance(forced_encoding, (list, dict)): + out = "" + for x in string: + try: + out += forced_encoding[x] + except Exception: + out += bytes((x,)).decode("charmap") + obj = TextStringObject(out) + obj._original_bytes = string + return obj + elif isinstance(forced_encoding, str): + if forced_encoding == "bytes": + return ByteStringObject(string) + obj = TextStringObject(string.decode(forced_encoding)) + obj._original_bytes = string + return obj + else: + try: + if string.startswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)): + retval = TextStringObject(string.decode("utf-16")) + retval._original_bytes = string + retval.autodetect_utf16 = True + retval.utf16_bom = string[:2] + return retval + if string.startswith(b"\x00"): + retval = TextStringObject(string.decode("utf-16be")) + retval._original_bytes = string + retval.autodetect_utf16 = True + retval.utf16_bom = codecs.BOM_UTF16_BE + return retval + if string[1:2] == b"\x00": + retval = TextStringObject(string.decode("utf-16le")) + retval._original_bytes = string + retval.autodetect_utf16 = True + retval.utf16_bom = codecs.BOM_UTF16_LE + return retval + + # This is probably a big performance hit here, but we need + # to convert string objects into the text/unicode-aware + # version if possible... and the only way to check if that's + # possible is to try. + # Some strings are strings, some are just byte arrays. + retval = TextStringObject(decode_pdfdocencoding(string)) + retval._original_bytes = string + retval.autodetect_pdfdocencoding = True + return retval + except UnicodeDecodeError: + return ByteStringObject(string) + else: + raise TypeError("create_string_object should have str or unicode arg") + + +def decode_pdfdocencoding(byte_array: bytes) -> str: + retval = "" + for b in byte_array: + c = _pdfdoc_encoding[b] + if c == "\u0000": + raise UnicodeDecodeError( + "pdfdocencoding", + bytearray(b), + -1, + -1, + "does not exist in translation table", + ) + retval += c + return retval diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_viewerpref.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_viewerpref.py new file mode 100644 index 00000000..72f89d9a --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/generic/_viewerpref.py @@ -0,0 +1,164 @@ +# Copyright (c) 2023, Pubpub-ZZ +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from typing import ( + Any, + List, + Optional, +) + +from ._base import BooleanObject, NameObject, NumberObject, is_null_or_none +from ._data_structures import ArrayObject, DictionaryObject + +f_obj = BooleanObject(False) + + +class ViewerPreferences(DictionaryObject): + def _get_bool(self, key: str, deft: Optional[BooleanObject]) -> BooleanObject: + return self.get(key, deft) + + def _set_bool(self, key: str, v: bool) -> None: + self[NameObject(key)] = BooleanObject(v is True) + + def _get_name(self, key: str, deft: Optional[NameObject]) -> Optional[NameObject]: + return self.get(key, deft) + + def _set_name(self, key: str, lst: List[str], v: NameObject) -> None: + if v[0] != "/": + raise ValueError(f"{v} is not starting with '/'") + if lst != [] and v not in lst: + raise ValueError(f"{v} is not par of acceptable values") + self[NameObject(key)] = NameObject(v) + + def _get_arr(self, key: str, deft: Optional[List[Any]]) -> NumberObject: + return self.get(key, None if deft is None else ArrayObject(deft)) + + def _set_arr(self, key: str, v: Optional[ArrayObject]) -> None: + if v is None: + try: + del self[NameObject(key)] + except KeyError: + pass + return + if not isinstance(v, ArrayObject): + raise ValueError("ArrayObject is expected") + self[NameObject(key)] = v + + def _get_int(self, key: str, deft: Optional[NumberObject]) -> NumberObject: + return self.get(key, deft) + + def _set_int(self, key: str, v: int) -> None: + self[NameObject(key)] = NumberObject(v) + + @property + def PRINT_SCALING(self) -> NameObject: + return NameObject("/PrintScaling") + + def __new__(cls: Any, value: Any = None) -> "ViewerPreferences": + def _add_prop_bool(key: str, deft: Optional[BooleanObject]) -> property: + return property( + lambda self: self._get_bool(key, deft), + lambda self, v: self._set_bool(key, v), + None, + f""" + Returns/Modify the status of {key}, Returns {deft} if not defined + """, + ) + + def _add_prop_name( + key: str, lst: List[str], deft: Optional[NameObject] + ) -> property: + return property( + lambda self: self._get_name(key, deft), + lambda self, v: self._set_name(key, lst, v), + None, + f""" + Returns/Modify the status of {key}, Returns {deft} if not defined. + Acceptable values: {lst} + """, + ) + + def _add_prop_arr(key: str, deft: Optional[ArrayObject]) -> property: + return property( + lambda self: self._get_arr(key, deft), + lambda self, v: self._set_arr(key, v), + None, + f""" + Returns/Modify the status of {key}, Returns {deft} if not defined + """, + ) + + def _add_prop_int(key: str, deft: Optional[int]) -> property: + return property( + lambda self: self._get_int(key, deft), + lambda self, v: self._set_int(key, v), + None, + f""" + Returns/Modify the status of {key}, Returns {deft} if not defined + """, + ) + + cls.hide_toolbar = _add_prop_bool("/HideToolbar", f_obj) + cls.hide_menubar = _add_prop_bool("/HideMenubar", f_obj) + cls.hide_windowui = _add_prop_bool("/HideWindowUI", f_obj) + cls.fit_window = _add_prop_bool("/FitWindow", f_obj) + cls.center_window = _add_prop_bool("/CenterWindow", f_obj) + cls.display_doctitle = _add_prop_bool("/DisplayDocTitle", f_obj) + + cls.non_fullscreen_pagemode = _add_prop_name( + "/NonFullScreenPageMode", + ["/UseNone", "/UseOutlines", "/UseThumbs", "/UseOC"], + NameObject("/UseNone"), + ) + cls.direction = _add_prop_name( + "/Direction", ["/L2R", "/R2L"], NameObject("/L2R") + ) + cls.view_area = _add_prop_name("/ViewArea", [], None) + cls.view_clip = _add_prop_name("/ViewClip", [], None) + cls.print_area = _add_prop_name("/PrintArea", [], None) + cls.print_clip = _add_prop_name("/PrintClip", [], None) + cls.print_scaling = _add_prop_name("/PrintScaling", [], None) + cls.duplex = _add_prop_name( + "/Duplex", ["/Simplex", "/DuplexFlipShortEdge", "/DuplexFlipLongEdge"], None + ) + cls.pick_tray_by_pdfsize = _add_prop_bool("/PickTrayByPDFSize", None) + cls.print_pagerange = _add_prop_arr("/PrintPageRange", None) + cls.num_copies = _add_prop_int("/NumCopies", None) + + cls.enforce = _add_prop_arr("/Enforce", ArrayObject()) + + return DictionaryObject.__new__(cls) + + def __init__(self, obj: Optional[DictionaryObject] = None) -> None: + super().__init__(self) + if not is_null_or_none(obj): + self.update(obj.items()) # type: ignore + try: + self.indirect_reference = obj.indirect_reference # type: ignore + except AttributeError: + pass diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/pagerange.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/pagerange.py new file mode 100644 index 00000000..47a72c72 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/pagerange.py @@ -0,0 +1,192 @@ +""" +Representation and utils for ranges of PDF file pages. + +Copyright (c) 2014, Steve Witham . +All rights reserved. This software is available under a BSD license; +see https://github.com/py-pdf/pypdf/blob/main/LICENSE +""" + +import re +from typing import Any, List, Tuple, Union + +from .errors import ParseError + +_INT_RE = r"(0|-?[1-9]\d*)" # A decimal int, don't allow "-0". +PAGE_RANGE_RE = f"^({_INT_RE}|({_INT_RE}?(:{_INT_RE}?(:{_INT_RE}?)?)))$" +# groups: 12 34 5 6 7 8 + + +class PageRange: + """ + A slice-like representation of a range of page indices. + + For example, page numbers, only starting at zero. + + The syntax is like what you would put between brackets [ ]. + The slice is one of the few Python types that can't be subclassed, + but this class converts to and from slices, and allows similar use. + + - PageRange(str) parses a string representing a page range. + - PageRange(slice) directly "imports" a slice. + - to_slice() gives the equivalent slice. + - str() and repr() allow printing. + - indices(n) is like slice.indices(n). + """ + + def __init__(self, arg: Union[slice, "PageRange", str]) -> None: + """ + Initialize with either a slice -- giving the equivalent page range, + or a PageRange object -- making a copy, + or a string like + "int", "[int]:[int]" or "[int]:[int]:[int]", + where the brackets indicate optional ints. + Remember, page indices start with zero. + Page range expression examples: + + : all pages. -1 last page. + 22 just the 23rd page. :-1 all but the last page. + 0:3 the first three pages. -2 second-to-last page. + :3 the first three pages. -2: last two pages. + 5: from the sixth page onward. -3:-1 third & second to last. + The third, "stride" or "step" number is also recognized. + ::2 0 2 4 ... to the end. 3:0:-1 3 2 1 but not 0. + 1:10:2 1 3 5 7 9 2::-1 2 1 0. + ::-1 all pages in reverse order. + Note the difference between this notation and arguments to slice(): + slice(3) means the first three pages; + PageRange("3") means the range of only the fourth page. + However PageRange(slice(3)) means the first three pages. + """ + if isinstance(arg, slice): + self._slice = arg + return + + if isinstance(arg, PageRange): + self._slice = arg.to_slice() + return + + m = isinstance(arg, str) and re.match(PAGE_RANGE_RE, arg) + if not m: + raise ParseError(arg) + elif m.group(2): + # Special case: just an int means a range of one page. + start = int(m.group(2)) + stop = start + 1 if start != -1 else None + self._slice = slice(start, stop) + else: + self._slice = slice(*[int(g) if g else None for g in m.group(4, 6, 8)]) + + @staticmethod + def valid(input: Any) -> bool: + """ + True if input is a valid initializer for a PageRange. + + Args: + input: A possible PageRange string or a PageRange object. + + Returns: + True, if the ``input`` is a valid PageRange. + """ + return isinstance(input, (slice, PageRange)) or ( + isinstance(input, str) and bool(re.match(PAGE_RANGE_RE, input)) + ) + + def to_slice(self) -> slice: + """Return the slice equivalent of this page range.""" + return self._slice + + def __str__(self) -> str: + """A string like "1:2:3".""" + s = self._slice + indices: Union[Tuple[int, int], Tuple[int, int, int]] + if s.step is None: + if s.start is not None and s.stop == s.start + 1: + return str(s.start) + + indices = s.start, s.stop + else: + indices = s.start, s.stop, s.step + return ":".join("" if i is None else str(i) for i in indices) + + def __repr__(self) -> str: + """A string like "PageRange('1:2:3')".""" + return "PageRange(" + repr(str(self)) + ")" + + def indices(self, n: int) -> Tuple[int, int, int]: + """ + Assuming a sequence of length n, calculate the start and stop indices, + and the stride length of the PageRange. + + See help(slice.indices). + + Args: + n: the length of the list of pages to choose from. + + Returns: + Arguments for range(). + """ + return self._slice.indices(n) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PageRange): + return False + return self._slice == other._slice + + def __add__(self, other: "PageRange") -> "PageRange": + if not isinstance(other, PageRange): + raise TypeError(f"Can't add PageRange and {type(other)}") + if self._slice.step is not None or other._slice.step is not None: + raise ValueError("Can't add PageRange with stride") + a = self._slice.start, self._slice.stop + b = other._slice.start, other._slice.stop + + if a[0] > b[0]: + a, b = b, a + + # Now a[0] is the smallest + if b[0] > a[1]: + # There is a gap between a and b. + raise ValueError("Can't add PageRanges with gap") + return PageRange(slice(a[0], max(a[1], b[1]))) + + +PAGE_RANGE_ALL = PageRange(":") # The range of all pages. + + +def parse_filename_page_ranges( + args: List[Union[str, PageRange, None]] +) -> List[Tuple[str, PageRange]]: + """ + Given a list of filenames and page ranges, return a list of (filename, page_range) pairs. + + Args: + args: A list where the first element is a filename. The other elements are + filenames, page-range expressions, slice objects, or PageRange objects. + A filename not followed by a page range indicates all pages of the file. + + Returns: + A list of (filename, page_range) pairs. + """ + pairs: List[Tuple[str, PageRange]] = [] + pdf_filename = None + did_page_range = False + for arg in args + [None]: + if PageRange.valid(arg): + if not pdf_filename: + raise ValueError( + "The first argument must be a filename, not a page range." + ) + + pairs.append((pdf_filename, PageRange(arg))) + did_page_range = True + else: + # New filename or end of list--do all of the previous file? + if pdf_filename and not did_page_range: + pairs.append((pdf_filename, PAGE_RANGE_ALL)) + + pdf_filename = arg + did_page_range = False + return pairs + + +PageRangeSpec = Union[str, PageRange, Tuple[int, int], Tuple[int, int, int], List[int]] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/papersizes.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/papersizes.py new file mode 100644 index 00000000..ed09f341 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/papersizes.py @@ -0,0 +1,52 @@ +"""Helper to get paper sizes.""" + +from typing import NamedTuple + + +class Dimensions(NamedTuple): + width: int + height: int + + +class PaperSize: + """(width, height) of the paper in portrait mode in pixels at 72 ppi.""" + + # Notes of how to calculate it: + # 1. Get the size of the paper in millimeters + # 2. Convert it to inches (25.4 millimeters is equal to 1 inch) + # 3. Convert it to pixels at 72dpi (1 inch is equal to 72 pixels) + + # All Din-A paper sizes follow this pattern: + # 2 x A(n - 1) = A(n) + # So the height of the next bigger one is the width of the smaller one + # The ratio is always approximately 1:2**0.5 + # Additionally, A0 is defined to have an area of 1 m**2 + # https://en.wikipedia.org/wiki/ISO_216 + # Be aware of rounding issues! + A0 = Dimensions(2384, 3370) # 841mm x 1189mm + A1 = Dimensions(1684, 2384) + A2 = Dimensions(1191, 1684) + A3 = Dimensions(842, 1191) + A4 = Dimensions( + 595, 842 + ) # Printer paper, documents - this is by far the most common + A5 = Dimensions(420, 595) # Paperback books + A6 = Dimensions(298, 420) # Postcards + A7 = Dimensions(210, 298) + A8 = Dimensions(147, 210) + + # Envelopes + C4 = Dimensions(649, 918) + + +_din_a = ( + PaperSize.A0, + PaperSize.A1, + PaperSize.A2, + PaperSize.A3, + PaperSize.A4, + PaperSize.A5, + PaperSize.A6, + PaperSize.A7, + PaperSize.A8, +) diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/py.typed b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/types.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/types.py new file mode 100644 index 00000000..e383dc7b --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/types.py @@ -0,0 +1,77 @@ +"""Helpers for working with PDF types.""" + +import sys +from typing import List, Literal, Union + +if sys.version_info[:2] >= (3, 10): + # Python 3.10+: https://www.python.org/dev/peps/pep-0484 + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +from .generic._base import NameObject, NullObject, NumberObject +from .generic._data_structures import ArrayObject, Destination +from .generic._outline import OutlineItem + +BorderArrayType: TypeAlias = List[Union[NameObject, NumberObject, ArrayObject]] +OutlineItemType: TypeAlias = Union[OutlineItem, Destination] +FitType: TypeAlias = Literal[ + "/XYZ", "/Fit", "/FitH", "/FitV", "/FitR", "/FitB", "/FitBH", "/FitBV" +] +# Those go with the FitType: They specify values for the fit +ZoomArgType: TypeAlias = Union[NumberObject, NullObject, float] +ZoomArgsType: TypeAlias = List[ZoomArgType] + +# Recursive types like the following are not yet supported by mypy: +# OutlineType = List[Union[Destination, "OutlineType"]] +# See https://github.com/python/mypy/issues/731 +# Hence use this for the moment: +OutlineType = List[Union[Destination, List[Union[Destination, List[Destination]]]]] + +LayoutType: TypeAlias = Literal[ + "/NoLayout", + "/SinglePage", + "/OneColumn", + "/TwoColumnLeft", + "/TwoColumnRight", + "/TwoPageLeft", + "/TwoPageRight", +] +PagemodeType: TypeAlias = Literal[ + "/UseNone", + "/UseOutlines", + "/UseThumbs", + "/FullScreen", + "/UseOC", + "/UseAttachments", +] +AnnotationSubtype: TypeAlias = Literal[ + "/Text", + "/Link", + "/FreeText", + "/Line", + "/Square", + "/Circle", + "/Polygon", + "/PolyLine", + "/Highlight", + "/Underline", + "/Squiggly", + "/StrikeOut", + "/Caret", + "/Stamp", + "/Ink", + "/Popup", + "/FileAttachment", + "/Sound", + "/Movie", + "/Screen", + "/Widget", + "/PrinterMark", + "/TrapNet", + "/Watermark", + "/3D", + "/Redact", + "/Projection", + "/RichMedia", +] diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/xmp.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/xmp.py new file mode 100644 index 00000000..0c4444fc --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/pypdf/xmp.py @@ -0,0 +1,392 @@ +""" +Anything related to Extensible Metadata Platform (XMP) metadata. + +https://en.wikipedia.org/wiki/Extensible_Metadata_Platform +""" + +import datetime +import decimal +import re +from typing import ( + Any, + Callable, + Dict, + Iterator, + List, + Optional, + TypeVar, + Union, +) +from xml.dom.minidom import Document, parseString +from xml.dom.minidom import Element as XmlElement +from xml.parsers.expat import ExpatError + +from ._utils import StreamType, deprecate_no_replacement +from .errors import PdfReadError +from .generic import ContentStream, PdfObject + +RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +DC_NAMESPACE = "http://purl.org/dc/elements/1.1/" +XMP_NAMESPACE = "http://ns.adobe.com/xap/1.0/" +PDF_NAMESPACE = "http://ns.adobe.com/pdf/1.3/" +XMPMM_NAMESPACE = "http://ns.adobe.com/xap/1.0/mm/" + +# What is the PDFX namespace, you might ask? +# It's documented here: https://github.com/adobe/xmp-docs/raw/master/XMPSpecifications/XMPSpecificationPart3.pdf +# This namespace is used to place "custom metadata" +# properties, which are arbitrary metadata properties with no semantic or +# documented meaning. +# +# Elements in the namespace are key/value-style storage, +# where the element name is the key and the content is the value. The keys +# are transformed into valid XML identifiers by substituting an invalid +# identifier character with \u2182 followed by the unicode hex ID of the +# original character. A key like "my car" is therefore "my\u21820020car". +# +# \u2182 is the unicode character \u{ROMAN NUMERAL TEN THOUSAND} +# +# The pdfx namespace should be avoided. +# A custom data schema and sensical XML elements could be used instead, as is +# suggested by Adobe's own documentation on XMP under "Extensibility of +# Schemas". +PDFX_NAMESPACE = "http://ns.adobe.com/pdfx/1.3/" + +iso8601 = re.compile( + """ + (?P[0-9]{4}) + (- + (?P[0-9]{2}) + (- + (?P[0-9]+) + (T + (?P[0-9]{2}): + (?P[0-9]{2}) + (:(?P[0-9]{2}(.[0-9]+)?))? + (?PZ|[-+][0-9]{2}:[0-9]{2}) + )? + )? + )? + """, + re.VERBOSE, +) + + +K = TypeVar("K") + + +def _identity(value: K) -> K: + return value + + +def _converter_date(value: str) -> datetime.datetime: + matches = iso8601.match(value) + if matches is None: + raise ValueError(f"Invalid date format: {value}") + year = int(matches.group("year")) + month = int(matches.group("month") or "1") + day = int(matches.group("day") or "1") + hour = int(matches.group("hour") or "0") + minute = int(matches.group("minute") or "0") + second = decimal.Decimal(matches.group("second") or "0") + seconds_dec = second.to_integral(decimal.ROUND_FLOOR) + milliseconds_dec = (second - seconds_dec) * 1_000_000 + + seconds = int(seconds_dec) + milliseconds = int(milliseconds_dec) + + tzd = matches.group("tzd") or "Z" + dt = datetime.datetime(year, month, day, hour, minute, seconds, milliseconds) + if tzd != "Z": + tzd_hours, tzd_minutes = (int(x) for x in tzd.split(":")) + tzd_hours *= -1 + if tzd_hours < 0: + tzd_minutes *= -1 + dt = dt + datetime.timedelta(hours=tzd_hours, minutes=tzd_minutes) + return dt + + +def _getter_bag( + namespace: str, name: str +) -> Callable[["XmpInformation"], Optional[List[str]]]: + def get(self: "XmpInformation") -> Optional[List[str]]: + cached = self.cache.get(namespace, {}).get(name) + if cached: + return cached + retval = [] + for element in self.get_element("", namespace, name): + bags = element.getElementsByTagNameNS(RDF_NAMESPACE, "Bag") + if len(bags): + for bag in bags: + for item in bag.getElementsByTagNameNS(RDF_NAMESPACE, "li"): + value = self._get_text(item) + retval.append(value) + ns_cache = self.cache.setdefault(namespace, {}) + ns_cache[name] = retval + return retval + + return get + + +def _getter_seq( + namespace: str, name: str, converter: Callable[[Any], Any] = _identity +) -> Callable[["XmpInformation"], Optional[List[Any]]]: + def get(self: "XmpInformation") -> Optional[List[Any]]: + cached = self.cache.get(namespace, {}).get(name) + if cached: + return cached + retval = [] + for element in self.get_element("", namespace, name): + seqs = element.getElementsByTagNameNS(RDF_NAMESPACE, "Seq") + if len(seqs): + for seq in seqs: + for item in seq.getElementsByTagNameNS(RDF_NAMESPACE, "li"): + value = self._get_text(item) + value = converter(value) + retval.append(value) + else: + value = converter(self._get_text(element)) + retval.append(value) + ns_cache = self.cache.setdefault(namespace, {}) + ns_cache[name] = retval + return retval + + return get + + +def _getter_langalt( + namespace: str, name: str +) -> Callable[["XmpInformation"], Optional[Dict[Any, Any]]]: + def get(self: "XmpInformation") -> Optional[Dict[Any, Any]]: + cached = self.cache.get(namespace, {}).get(name) + if cached: + return cached + retval = {} + for element in self.get_element("", namespace, name): + alts = element.getElementsByTagNameNS(RDF_NAMESPACE, "Alt") + if len(alts): + for alt in alts: + for item in alt.getElementsByTagNameNS(RDF_NAMESPACE, "li"): + value = self._get_text(item) + retval[item.getAttribute("xml:lang")] = value + else: + retval["x-default"] = self._get_text(element) + ns_cache = self.cache.setdefault(namespace, {}) + ns_cache[name] = retval + return retval + + return get + + +def _getter_single( + namespace: str, name: str, converter: Callable[[str], Any] = _identity +) -> Callable[["XmpInformation"], Optional[Any]]: + def get(self: "XmpInformation") -> Optional[Any]: + cached = self.cache.get(namespace, {}).get(name) + if cached: + return cached + value = None + for element in self.get_element("", namespace, name): + if element.nodeType == element.ATTRIBUTE_NODE: + value = element.nodeValue + else: + value = self._get_text(element) + break + if value is not None: + value = converter(value) + ns_cache = self.cache.setdefault(namespace, {}) + ns_cache[name] = value + return value + + return get + + +class XmpInformation(PdfObject): + """ + An object that represents Extensible Metadata Platform (XMP) metadata. + Usually accessed by :py:attr:`xmp_metadata()`. + + Raises: + PdfReadError: if XML is invalid + """ + + def __init__(self, stream: ContentStream) -> None: + self.stream = stream + try: + data = self.stream.get_data() + doc_root: Document = parseString(data) # noqa: S318 + except ExpatError as e: + raise PdfReadError(f"XML in XmpInformation was invalid: {e}") + self.rdf_root: XmlElement = doc_root.getElementsByTagNameNS( + RDF_NAMESPACE, "RDF" + )[0] + self.cache: Dict[Any, Any] = {} + + def write_to_stream( + self, stream: StreamType, encryption_key: Union[None, str, bytes] = None + ) -> None: + if encryption_key is not None: # deprecated + deprecate_no_replacement( + "the encryption_key parameter of write_to_stream", "5.0.0" + ) + self.stream.write_to_stream(stream) + + def get_element(self, about_uri: str, namespace: str, name: str) -> Iterator[Any]: + for desc in self.rdf_root.getElementsByTagNameNS(RDF_NAMESPACE, "Description"): + if desc.getAttributeNS(RDF_NAMESPACE, "about") == about_uri: + attr = desc.getAttributeNodeNS(namespace, name) + if attr is not None: + yield attr + yield from desc.getElementsByTagNameNS(namespace, name) + + def get_nodes_in_namespace(self, about_uri: str, namespace: str) -> Iterator[Any]: + for desc in self.rdf_root.getElementsByTagNameNS(RDF_NAMESPACE, "Description"): + if desc.getAttributeNS(RDF_NAMESPACE, "about") == about_uri: + for i in range(desc.attributes.length): + attr = desc.attributes.item(i) + if attr.namespaceURI == namespace: + yield attr + for child in desc.childNodes: + if child.namespaceURI == namespace: + yield child + + def _get_text(self, element: XmlElement) -> str: + text = "" + for child in element.childNodes: + if child.nodeType == child.TEXT_NODE: + text += child.data + return text + + dc_contributor = property(_getter_bag(DC_NAMESPACE, "contributor")) + """ + Contributors to the resource (other than the authors). + + An unsorted array of names. + """ + + dc_coverage = property(_getter_single(DC_NAMESPACE, "coverage")) + """Text describing the extent or scope of the resource.""" + + dc_creator = property(_getter_seq(DC_NAMESPACE, "creator")) + """A sorted array of names of the authors of the resource, listed in order + of precedence.""" + + dc_date = property(_getter_seq(DC_NAMESPACE, "date", _converter_date)) + """ + A sorted array of dates (datetime.datetime instances) of significance to + the resource. + + The dates and times are in UTC. + """ + + dc_description = property(_getter_langalt(DC_NAMESPACE, "description")) + """A language-keyed dictionary of textual descriptions of the content of the + resource.""" + + dc_format = property(_getter_single(DC_NAMESPACE, "format")) + """The mime-type of the resource.""" + + dc_identifier = property(_getter_single(DC_NAMESPACE, "identifier")) + """Unique identifier of the resource.""" + + dc_language = property(_getter_bag(DC_NAMESPACE, "language")) + """An unordered array specifying the languages used in the resource.""" + + dc_publisher = property(_getter_bag(DC_NAMESPACE, "publisher")) + """An unordered array of publisher names.""" + + dc_relation = property(_getter_bag(DC_NAMESPACE, "relation")) + """An unordered array of text descriptions of relationships to other + documents.""" + + dc_rights = property(_getter_langalt(DC_NAMESPACE, "rights")) + """A language-keyed dictionary of textual descriptions of the rights the + user has to this resource.""" + + dc_source = property(_getter_single(DC_NAMESPACE, "source")) + """Unique identifier of the work from which this resource was derived.""" + + dc_subject = property(_getter_bag(DC_NAMESPACE, "subject")) + """An unordered array of descriptive phrases or keywords that specify the + topic of the content of the resource.""" + + dc_title = property(_getter_langalt(DC_NAMESPACE, "title")) + """A language-keyed dictionary of the title of the resource.""" + + dc_type = property(_getter_bag(DC_NAMESPACE, "type")) + """An unordered array of textual descriptions of the document type.""" + + pdf_keywords = property(_getter_single(PDF_NAMESPACE, "Keywords")) + """An unformatted text string representing document keywords.""" + + pdf_pdfversion = property(_getter_single(PDF_NAMESPACE, "PDFVersion")) + """The PDF file version, for example 1.0 or 1.3.""" + + pdf_producer = property(_getter_single(PDF_NAMESPACE, "Producer")) + """The name of the tool that created the PDF document.""" + + xmp_create_date = property( + _getter_single(XMP_NAMESPACE, "CreateDate", _converter_date) + ) + """ + The date and time the resource was originally created. + + The date and time are returned as a UTC datetime.datetime object. + """ + + xmp_modify_date = property( + _getter_single(XMP_NAMESPACE, "ModifyDate", _converter_date) + ) + """ + The date and time the resource was last modified. + + The date and time are returned as a UTC datetime.datetime object. + """ + + xmp_metadata_date = property( + _getter_single(XMP_NAMESPACE, "MetadataDate", _converter_date) + ) + """ + The date and time that any metadata for this resource was last changed. + + The date and time are returned as a UTC datetime.datetime object. + """ + + xmp_creator_tool = property(_getter_single(XMP_NAMESPACE, "CreatorTool")) + """The name of the first known tool used to create the resource.""" + + xmpmm_document_id = property(_getter_single(XMPMM_NAMESPACE, "DocumentID")) + """The common identifier for all versions and renditions of this resource.""" + + xmpmm_instance_id = property(_getter_single(XMPMM_NAMESPACE, "InstanceID")) + """An identifier for a specific incarnation of a document, updated each + time a file is saved.""" + + @property + def custom_properties(self) -> Dict[Any, Any]: + """ + Retrieve custom metadata properties defined in the undocumented pdfx + metadata schema. + + Returns: + A dictionary of key/value items for custom metadata properties. + """ + if not hasattr(self, "_custom_properties"): + self._custom_properties = {} + for node in self.get_nodes_in_namespace("", PDFX_NAMESPACE): + key = node.localName + while True: + # see documentation about PDFX_NAMESPACE earlier in file + idx = key.find("\u2182") + if idx == -1: + break + key = ( + key[:idx] + + chr(int(key[idx + 1 : idx + 5], base=16)) + + key[idx + 5 :] + ) + if node.nodeType == node.ATTRIBUTE_NODE: + value = node.nodeValue + else: + value = self._get_text(node) + self._custom_properties[key] = value + return self._custom_properties diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/INSTALLER b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/LICENSE b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/LICENSE new file mode 100644 index 00000000..f26bcf4d --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/LICENSE @@ -0,0 +1,279 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/METADATA b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/METADATA new file mode 100644 index 00000000..f15e2b38 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/METADATA @@ -0,0 +1,67 @@ +Metadata-Version: 2.1 +Name: typing_extensions +Version: 4.12.2 +Summary: Backported and Experimental Type Hints for Python 3.8+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Topic :: Software Development +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md +Project-URL: Documentation, https://typing-extensions.readthedocs.io/ +Project-URL: Home, https://github.com/python/typing_extensions +Project-URL: Q & A, https://github.com/python/typing/discussions +Project-URL: Repository, https://github.com/python/typing_extensions + +# Typing Extensions + +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing) + +[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) – +[PyPI](https://pypi.org/project/typing-extensions/) + +## Overview + +The `typing_extensions` module serves two related purposes: + +- Enable use of new type system features on older Python versions. For example, + `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows + users on previous Python versions to use it too. +- Enable experimentation with new type system PEPs before they are accepted and + added to the `typing` module. + +`typing_extensions` is treated specially by static type checkers such as +mypy and pyright. Objects defined in `typing_extensions` are treated the same +way as equivalent forms in `typing`. + +`typing_extensions` uses +[Semantic Versioning](https://semver.org/). The +major version will be incremented only for backwards-incompatible changes. +Therefore, it's safe to depend +on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`, +where `x.y` is the first version that includes all features you need. + +## Included items + +See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a +complete listing of module contents. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md) +for how to contribute to `typing_extensions`. + diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/RECORD b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/RECORD new file mode 100644 index 00000000..cac93e4c --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/RECORD @@ -0,0 +1,7 @@ +__pycache__/typing_extensions.cpython-38.pyc,, +typing_extensions-4.12.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +typing_extensions-4.12.2.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936 +typing_extensions-4.12.2.dist-info/METADATA,sha256=BeUQIa8cnYbrjWx-N8TOznM9UGW5Gm2DicVpDtRA8W0,3018 +typing_extensions-4.12.2.dist-info/RECORD,, +typing_extensions-4.12.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +typing_extensions.py,sha256=gwekpyG9DVG3lxWKX4ni8u7nk3We5slG98mA9F3DJQw,134451 diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/WHEEL b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/WHEEL new file mode 100644 index 00000000..3b5e64b5 --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions-4.12.2.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions.py b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions.py new file mode 100644 index 00000000..dec429ca --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tmp/custom_chunking_lambda_package/typing_extensions.py @@ -0,0 +1,3641 @@ +import abc +import collections +import collections.abc +import contextlib +import functools +import inspect +import operator +import sys +import types as _types +import typing +import warnings + +__all__ = [ + # Super-special typing primitives. + 'Any', + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'ParamSpecArgs', + 'ParamSpecKwargs', + 'Self', + 'Type', + 'TypeVar', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'Buffer', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'NamedTuple', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsAbs', + 'SupportsBytes', + 'SupportsComplex', + 'SupportsFloat', + 'SupportsIndex', + 'SupportsInt', + 'SupportsRound', + + # One-off things. + 'Annotated', + 'assert_never', + 'assert_type', + 'clear_overloads', + 'dataclass_transform', + 'deprecated', + 'Doc', + 'get_overloads', + 'final', + 'get_args', + 'get_origin', + 'get_original_bases', + 'get_protocol_members', + 'get_type_hints', + 'IntVar', + 'is_protocol', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'override', + 'Protocol', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeAliasType', + 'TypeGuard', + 'TypeIs', + 'TYPE_CHECKING', + 'Never', + 'NoReturn', + 'ReadOnly', + 'Required', + 'NotRequired', + + # Pure aliases, have always been in typing + 'AbstractSet', + 'AnyStr', + 'BinaryIO', + 'Callable', + 'Collection', + 'Container', + 'Dict', + 'ForwardRef', + 'FrozenSet', + 'Generator', + 'Generic', + 'Hashable', + 'IO', + 'ItemsView', + 'Iterable', + 'Iterator', + 'KeysView', + 'List', + 'Mapping', + 'MappingView', + 'Match', + 'MutableMapping', + 'MutableSequence', + 'MutableSet', + 'NoDefault', + 'Optional', + 'Pattern', + 'Reversible', + 'Sequence', + 'Set', + 'Sized', + 'TextIO', + 'Tuple', + 'Union', + 'ValuesView', + 'cast', + 'no_type_check', + 'no_type_check_decorator', +] + +# for backward compatibility +PEP_560 = True +GenericMeta = type +_PEP_696_IMPLEMENTED = sys.version_info >= (3, 13, 0, "beta") + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + + +class _Sentinel: + def __repr__(self): + return "" + + +_marker = _Sentinel() + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +NoReturn = typing.NoReturn + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +if sys.version_info >= (3, 11): + from typing import Any +else: + + class _AnyMeta(type): + def __instancecheck__(self, obj): + if self is Any: + raise TypeError("typing_extensions.Any cannot be used with isinstance()") + return super().__instancecheck__(obj) + + def __repr__(self): + if self is Any: + return "typing_extensions.Any" + return super().__repr__() + + class Any(metaclass=_AnyMeta): + """Special type indicating an unconstrained type. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + checks. + """ + def __new__(cls, *args, **kwargs): + if cls is Any: + raise TypeError("Any cannot be instantiated") + return super().__new__(cls, *args, **kwargs) + + +ClassVar = typing.ClassVar + + +class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + +Final = typing.Final + +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +def IntVar(name): + return typing.TypeVar(name) + + +# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8 +if sys.version_info >= (3, 10, 1): + Literal = typing.Literal +else: + def _flatten_literal_params(parameters): + """An internal helper for Literal creation: flatten Literals among parameters""" + params = [] + for p in parameters: + if isinstance(p, _LiteralGenericAlias): + params.extend(p.__args__) + else: + params.append(p) + return tuple(params) + + def _value_and_type_iter(params): + for p in params: + yield p, type(p) + + class _LiteralGenericAlias(typing._GenericAlias, _root=True): + def __eq__(self, other): + if not isinstance(other, _LiteralGenericAlias): + return NotImplemented + these_args_deduped = set(_value_and_type_iter(self.__args__)) + other_args_deduped = set(_value_and_type_iter(other.__args__)) + return these_args_deduped == other_args_deduped + + def __hash__(self): + return hash(frozenset(_value_and_type_iter(self.__args__))) + + class _LiteralForm(_ExtensionsSpecialForm, _root=True): + def __init__(self, doc: str): + self._name = 'Literal' + self._doc = self.__doc__ = doc + + def __getitem__(self, parameters): + if not isinstance(parameters, tuple): + parameters = (parameters,) + + parameters = _flatten_literal_params(parameters) + + val_type_pairs = list(_value_and_type_iter(parameters)) + try: + deduped_pairs = set(val_type_pairs) + except TypeError: + # unhashable parameters + pass + else: + # similar logic to typing._deduplicate on Python 3.9+ + if len(deduped_pairs) < len(val_type_pairs): + new_parameters = [] + for pair in val_type_pairs: + if pair in deduped_pairs: + new_parameters.append(pair[0]) + deduped_pairs.remove(pair) + assert not deduped_pairs, deduped_pairs + parameters = tuple(new_parameters) + + return _LiteralGenericAlias(self, parameters) + + Literal = _LiteralForm(doc="""\ + A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") + + +_overload_dummy = typing._overload_dummy + + +if hasattr(typing, "get_overloads"): # 3.11+ + overload = typing.overload + get_overloads = typing.get_overloads + clear_overloads = typing.clear_overloads +else: + # {module: {qualname: {firstlineno: func}}} + _overload_registry = collections.defaultdict( + functools.partial(collections.defaultdict, dict) + ) + + def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + + The overloads for a function can be retrieved at runtime using the + get_overloads() function. + """ + # classmethod and staticmethod + f = getattr(func, "__func__", func) + try: + _overload_registry[f.__module__][f.__qualname__][ + f.__code__.co_firstlineno + ] = func + except AttributeError: + # Not a normal function; ignore. + pass + return _overload_dummy + + def get_overloads(func): + """Return all defined overloads for *func* as a sequence.""" + # classmethod and staticmethod + f = getattr(func, "__func__", func) + if f.__module__ not in _overload_registry: + return [] + mod_dict = _overload_registry[f.__module__] + if f.__qualname__ not in mod_dict: + return [] + return list(mod_dict[f.__qualname__].values()) + + def clear_overloads(): + """Clear all overloads in the registry.""" + _overload_registry.clear() + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +Deque = typing.Deque +DefaultDict = typing.DefaultDict +OrderedDict = typing.OrderedDict +Counter = typing.Counter +ChainMap = typing.ChainMap +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +if sys.version_info >= (3, 13, 0, "beta"): + from typing import AsyncContextManager, AsyncGenerator, ContextManager, Generator +else: + def _is_dunder(attr): + return attr.startswith('__') and attr.endswith('__') + + # Python <3.9 doesn't have typing._SpecialGenericAlias + _special_generic_alias_base = getattr( + typing, "_SpecialGenericAlias", typing._GenericAlias + ) + + class _SpecialGenericAlias(_special_generic_alias_base, _root=True): + def __init__(self, origin, nparams, *, inst=True, name=None, defaults=()): + if _special_generic_alias_base is typing._GenericAlias: + # Python <3.9 + self.__origin__ = origin + self._nparams = nparams + super().__init__(origin, nparams, special=True, inst=inst, name=name) + else: + # Python >= 3.9 + super().__init__(origin, nparams, inst=inst, name=name) + self._defaults = defaults + + def __setattr__(self, attr, val): + allowed_attrs = {'_name', '_inst', '_nparams', '_defaults'} + if _special_generic_alias_base is typing._GenericAlias: + # Python <3.9 + allowed_attrs.add("__origin__") + if _is_dunder(attr) or attr in allowed_attrs: + object.__setattr__(self, attr, val) + else: + setattr(self.__origin__, attr, val) + + @typing._tp_cache + def __getitem__(self, params): + if not isinstance(params, tuple): + params = (params,) + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) + if ( + self._defaults + and len(params) < self._nparams + and len(params) + len(self._defaults) >= self._nparams + ): + params = (*params, *self._defaults[len(params) - self._nparams:]) + actual_len = len(params) + + if actual_len != self._nparams: + if self._defaults: + expected = f"at least {self._nparams - len(self._defaults)}" + else: + expected = str(self._nparams) + if not self._nparams: + raise TypeError(f"{self} is not a generic class") + raise TypeError( + f"Too {'many' if actual_len > self._nparams else 'few'}" + f" arguments for {self};" + f" actual {actual_len}, expected {expected}" + ) + return self.copy_with(params) + + _NoneType = type(None) + Generator = _SpecialGenericAlias( + collections.abc.Generator, 3, defaults=(_NoneType, _NoneType) + ) + AsyncGenerator = _SpecialGenericAlias( + collections.abc.AsyncGenerator, 2, defaults=(_NoneType,) + ) + ContextManager = _SpecialGenericAlias( + contextlib.AbstractContextManager, + 2, + name="ContextManager", + defaults=(typing.Optional[bool],) + ) + AsyncContextManager = _SpecialGenericAlias( + contextlib.AbstractAsyncContextManager, + 2, + name="AsyncContextManager", + defaults=(typing.Optional[bool],) + ) + + +_PROTO_ALLOWLIST = { + 'collections.abc': [ + 'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer', + ], + 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], + 'typing_extensions': ['Buffer'], +} + + +_EXCLUDED_ATTRS = frozenset(typing.EXCLUDED_ATTRIBUTES) | { + "__match_args__", "__protocol_attrs__", "__non_callable_proto_members__", + "__final__", +} + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in {'Protocol', 'Generic'}: + continue + annotations = getattr(base, '__annotations__', {}) + for attr in (*base.__dict__, *annotations): + if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS): + attrs.add(attr) + return attrs + + +def _caller(depth=2): + try: + return sys._getframe(depth).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): # For platforms without _getframe() + return None + + +# `__match_args__` attribute was removed from protocol members in 3.13, +# we want to backport this change to older Python versions. +if sys.version_info >= (3, 13): + Protocol = typing.Protocol +else: + def _allow_reckless_class_checks(depth=3): + """Allow instance and class checks for special stdlib modules. + The abc and functools modules indiscriminately call isinstance() and + issubclass() on the whole MRO of a user class, which may contain protocols. + """ + return _caller(depth) in {'abc', 'functools', None} + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + def _type_check_issubclass_arg_1(arg): + """Raise TypeError if `arg` is not an instance of `type` + in `issubclass(arg, )`. + + In most cases, this is verified by type.__subclasscheck__. + Checking it again unnecessarily would slow down issubclass() checks, + so, we don't perform this check unless we absolutely have to. + + For various error paths, however, + we want to ensure that *this* error message is shown to the user + where relevant, rather than a typing.py-specific error message. + """ + if not isinstance(arg, type): + # Same error message as for issubclass(1, int). + raise TypeError('issubclass() arg 1 must be a class') + + # Inheriting from typing._ProtocolMeta isn't actually desirable, + # but is necessary to allow typing.Protocol and typing_extensions.Protocol + # to mix without getting TypeErrors about "metaclass conflict" + class _ProtocolMeta(type(typing.Protocol)): + # This metaclass is somewhat unfortunate, + # but is necessary for several reasons... + # + # NOTE: DO NOT call super() in any methods in this class + # That would call the methods on typing._ProtocolMeta on Python 3.8-3.11 + # and those are slow + def __new__(mcls, name, bases, namespace, **kwargs): + if name == "Protocol" and len(bases) < 2: + pass + elif {Protocol, typing.Protocol} & set(bases): + for base in bases: + if not ( + base in {object, typing.Generic, Protocol, typing.Protocol} + or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, []) + or is_protocol(base) + ): + raise TypeError( + f"Protocols can only inherit from other protocols, " + f"got {base!r}" + ) + return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs) + + def __init__(cls, *args, **kwargs): + abc.ABCMeta.__init__(cls, *args, **kwargs) + if getattr(cls, "_is_protocol", False): + cls.__protocol_attrs__ = _get_protocol_attrs(cls) + + def __subclasscheck__(cls, other): + if cls is Protocol: + return type.__subclasscheck__(cls, other) + if ( + getattr(cls, '_is_protocol', False) + and not _allow_reckless_class_checks() + ): + if not getattr(cls, '_is_runtime_protocol', False): + _type_check_issubclass_arg_1(other) + raise TypeError( + "Instance and class checks can only be used with " + "@runtime_checkable protocols" + ) + if ( + # this attribute is set by @runtime_checkable: + cls.__non_callable_proto_members__ + and cls.__dict__.get("__subclasshook__") is _proto_hook + ): + _type_check_issubclass_arg_1(other) + non_method_attrs = sorted(cls.__non_callable_proto_members__) + raise TypeError( + "Protocols with non-method members don't support issubclass()." + f" Non-method members: {str(non_method_attrs)[1:-1]}." + ) + return abc.ABCMeta.__subclasscheck__(cls, other) + + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if cls is Protocol: + return type.__instancecheck__(cls, instance) + if not getattr(cls, "_is_protocol", False): + # i.e., it's a concrete subclass of a protocol + return abc.ABCMeta.__instancecheck__(cls, instance) + + if ( + not getattr(cls, '_is_runtime_protocol', False) and + not _allow_reckless_class_checks() + ): + raise TypeError("Instance and class checks can only be used with" + " @runtime_checkable protocols") + + if abc.ABCMeta.__instancecheck__(cls, instance): + return True + + for attr in cls.__protocol_attrs__: + try: + val = inspect.getattr_static(instance, attr) + except AttributeError: + break + # this attribute is set by @runtime_checkable: + if val is None and attr not in cls.__non_callable_proto_members__: + break + else: + return True + + return False + + def __eq__(cls, other): + # Hack so that typing.Generic.__class_getitem__ + # treats typing_extensions.Protocol + # as equivalent to typing.Protocol + if abc.ABCMeta.__eq__(cls, other) is True: + return True + return cls is Protocol and other is typing.Protocol + + # This has to be defined, or the abc-module cache + # complains about classes with this metaclass being unhashable, + # if we define only __eq__! + def __hash__(cls) -> int: + return type.__hash__(cls) + + @classmethod + def _proto_hook(cls, other): + if not cls.__dict__.get('_is_protocol', False): + return NotImplemented + + for attr in cls.__protocol_attrs__: + for base in other.__mro__: + # Check if the members appears in the class dictionary... + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + + # ...or in annotations, if it is a sub-protocol. + annotations = getattr(base, '__annotations__', {}) + if ( + isinstance(annotations, collections.abc.Mapping) + and attr in annotations + and is_protocol(other) + ): + break + else: + return NotImplemented + return True + + class Protocol(typing.Generic, metaclass=_ProtocolMeta): + __doc__ = typing.Protocol.__doc__ + __slots__ = () + _is_protocol = True + _is_runtime_protocol = False + + def __init_subclass__(cls, *args, **kwargs): + super().__init_subclass__(*args, **kwargs) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', False): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # Prohibit instantiation for protocol classes + if cls._is_protocol and cls.__init__ is Protocol.__init__: + cls.__init__ = _no_init + + +if sys.version_info >= (3, 13): + runtime_checkable = typing.runtime_checkable +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol. + + Such protocol can be used with isinstance() and issubclass(). + Raise TypeError if applied to a non-protocol class. + This allows a simple-minded structural check very similar to + one trick ponies in collections.abc such as Iterable. + + For example:: + + @runtime_checkable + class Closable(Protocol): + def close(self): ... + + assert isinstance(open('/some/file'), Closable) + + Warning: this will check only the presence of the required methods, + not their type signatures! + """ + if not issubclass(cls, typing.Generic) or not getattr(cls, '_is_protocol', False): + raise TypeError(f'@runtime_checkable can be only applied to protocol classes,' + f' got {cls!r}') + cls._is_runtime_protocol = True + + # typing.Protocol classes on <=3.11 break if we execute this block, + # because typing.Protocol classes on <=3.11 don't have a + # `__protocol_attrs__` attribute, and this block relies on the + # `__protocol_attrs__` attribute. Meanwhile, typing.Protocol classes on 3.12.2+ + # break if we *don't* execute this block, because *they* assume that all + # protocol classes have a `__non_callable_proto_members__` attribute + # (which this block sets) + if isinstance(cls, _ProtocolMeta) or sys.version_info >= (3, 12, 2): + # PEP 544 prohibits using issubclass() + # with protocols that have non-method members. + # See gh-113320 for why we compute this attribute here, + # rather than in `_ProtocolMeta.__init__` + cls.__non_callable_proto_members__ = set() + for attr in cls.__protocol_attrs__: + try: + is_callable = callable(getattr(cls, attr, None)) + except Exception as e: + raise TypeError( + f"Failed to determine whether protocol member {attr!r} " + "is a method member" + ) from e + else: + if not is_callable: + cls.__non_callable_proto_members__.add(attr) + + return cls + + +# The "runtime" alias exists for backwards compatibility. +runtime = runtime_checkable + + +# Our version of runtime-checkable protocols is faster on Python 3.8-3.11 +if sys.version_info >= (3, 12): + SupportsInt = typing.SupportsInt + SupportsFloat = typing.SupportsFloat + SupportsComplex = typing.SupportsComplex + SupportsBytes = typing.SupportsBytes + SupportsIndex = typing.SupportsIndex + SupportsAbs = typing.SupportsAbs + SupportsRound = typing.SupportsRound +else: + @runtime_checkable + class SupportsInt(Protocol): + """An ABC with one abstract method __int__.""" + __slots__ = () + + @abc.abstractmethod + def __int__(self) -> int: + pass + + @runtime_checkable + class SupportsFloat(Protocol): + """An ABC with one abstract method __float__.""" + __slots__ = () + + @abc.abstractmethod + def __float__(self) -> float: + pass + + @runtime_checkable + class SupportsComplex(Protocol): + """An ABC with one abstract method __complex__.""" + __slots__ = () + + @abc.abstractmethod + def __complex__(self) -> complex: + pass + + @runtime_checkable + class SupportsBytes(Protocol): + """An ABC with one abstract method __bytes__.""" + __slots__ = () + + @abc.abstractmethod + def __bytes__(self) -> bytes: + pass + + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + @runtime_checkable + class SupportsAbs(Protocol[T_co]): + """ + An ABC with one abstract method __abs__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __abs__(self) -> T_co: + pass + + @runtime_checkable + class SupportsRound(Protocol[T_co]): + """ + An ABC with one abstract method __round__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __round__(self, ndigits: int = 0) -> T_co: + pass + + +def _ensure_subclassable(mro_entries): + def inner(func): + if sys.implementation.name == "pypy" and sys.version_info < (3, 9): + cls_dict = { + "__call__": staticmethod(func), + "__mro_entries__": staticmethod(mro_entries) + } + t = type(func.__name__, (), cls_dict) + return functools.update_wrapper(t(), func) + else: + func.__mro_entries__ = mro_entries + return func + return inner + + +# Update this to something like >=3.13.0b1 if and when +# PEP 728 is implemented in CPython +_PEP_728_IMPLEMENTED = False + +if _PEP_728_IMPLEMENTED: + # The standard library TypedDict in Python 3.8 does not store runtime information + # about which (if any) keys are optional. See https://bugs.python.org/issue38834 + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. + # Aaaand on 3.12 we add __orig_bases__ to TypedDict + # to enable better runtime introspection. + # On 3.13 we deprecate some odd ways of creating TypedDicts. + # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier. + # PEP 728 (still pending) makes more changes. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + # 3.10.0 and later + _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters + + def _get_typeddict_qualifiers(annotation_type): + while True: + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + else: + break + elif annotation_origin is Required: + yield Required + annotation_type, = get_args(annotation_type) + elif annotation_origin is NotRequired: + yield NotRequired + annotation_type, = get_args(annotation_type) + elif annotation_origin is ReadOnly: + yield ReadOnly + annotation_type, = get_args(annotation_type) + else: + break + + class _TypedDictMeta(type): + def __new__(cls, name, bases, ns, *, total=True, closed=False): + """Create new typed dict class object. + + This method is called when TypedDict is subclassed, + or when TypedDict is instantiated. This way + TypedDict supports all three syntax forms described in its docstring. + Subclasses and instances of TypedDict return actual dictionaries. + """ + for base in bases: + if type(base) is not _TypedDictMeta and base is not typing.Generic: + raise TypeError('cannot inherit from both a TypedDict type ' + 'and a non-TypedDict base class') + + if any(issubclass(b, typing.Generic) for b in bases): + generic_base = (typing.Generic,) + else: + generic_base = () + + # typing.py generally doesn't let you inherit from plain Generic, unless + # the name of the class happens to be "Protocol" + tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns) + tp_dict.__name__ = name + if tp_dict.__qualname__ == "Protocol": + tp_dict.__qualname__ = name + + if not hasattr(tp_dict, '__orig_bases__'): + tp_dict.__orig_bases__ = bases + + annotations = {} + if "__annotations__" in ns: + own_annotations = ns["__annotations__"] + elif "__annotate__" in ns: + # TODO: Use inspect.VALUE here, and make the annotations lazily evaluated + own_annotations = ns["__annotate__"](1) + else: + own_annotations = {} + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + if _TAKES_MODULE: + own_annotations = { + n: typing._type_check(tp, msg, module=tp_dict.__module__) + for n, tp in own_annotations.items() + } + else: + own_annotations = { + n: typing._type_check(tp, msg) + for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + readonly_keys = set() + mutable_keys = set() + extra_items_type = None + + for base in bases: + base_dict = base.__dict__ + + annotations.update(base_dict.get('__annotations__', {})) + required_keys.update(base_dict.get('__required_keys__', ())) + optional_keys.update(base_dict.get('__optional_keys__', ())) + readonly_keys.update(base_dict.get('__readonly_keys__', ())) + mutable_keys.update(base_dict.get('__mutable_keys__', ())) + base_extra_items_type = base_dict.get('__extra_items__', None) + if base_extra_items_type is not None: + extra_items_type = base_extra_items_type + + if closed and extra_items_type is None: + extra_items_type = Never + if closed and "__extra_items__" in own_annotations: + annotation_type = own_annotations.pop("__extra_items__") + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + if Required in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "Required" + ) + if NotRequired in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "NotRequired" + ) + extra_items_type = annotation_type + + annotations.update(own_annotations) + for annotation_key, annotation_type in own_annotations.items(): + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + + if Required in qualifiers: + required_keys.add(annotation_key) + elif NotRequired in qualifiers: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + if ReadOnly in qualifiers: + mutable_keys.discard(annotation_key) + readonly_keys.add(annotation_key) + else: + mutable_keys.add(annotation_key) + readonly_keys.discard(annotation_key) + + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + tp_dict.__readonly_keys__ = frozenset(readonly_keys) + tp_dict.__mutable_keys__ = frozenset(mutable_keys) + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + tp_dict.__closed__ = closed + tp_dict.__extra_items__ = extra_items_type + return tp_dict + + __call__ = dict # static method + + def __subclasscheck__(cls, other): + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + + __instancecheck__ = __subclasscheck__ + + _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) + + @_ensure_subclassable(lambda bases: (_TypedDict,)) + def TypedDict(typename, fields=_marker, /, *, total=True, closed=False, **kwargs): + """A simple typed namespace. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type such that a type checker will expect all + instances to have a certain set of keys, where each key is + associated with a value of a consistent type. This expectation + is not checked at runtime. + + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports an additional equivalent form:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + By default, all keys must be present in a TypedDict. It is possible + to override this by specifying totality:: + + class Point2D(TypedDict, total=False): + x: int + y: int + + This means that a Point2D TypedDict can have any of the keys omitted. A type + checker is only expected to support a literal False or True as the value of + the total argument. True is the default, and makes all items defined in the + class body be required. + + The Required and NotRequired special forms can also be used to mark + individual keys as being required or not required:: + + class Point2D(TypedDict): + x: int # the "x" key must always be present (Required is the default) + y: NotRequired[int] # the "y" key can be omitted + + See PEP 655 for more details on Required and NotRequired. + """ + if fields is _marker or fields is None: + if fields is _marker: + deprecated_thing = "Failing to pass a value for the 'fields' parameter" + else: + deprecated_thing = "Passing `None` as the 'fields' parameter" + + example = f"`{typename} = TypedDict({typename!r}, {{}})`" + deprecation_msg = ( + f"{deprecated_thing} is deprecated and will be disallowed in " + "Python 3.15. To create a TypedDict class with 0 fields " + "using the functional syntax, pass an empty dictionary, e.g. " + ) + example + "." + warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) + if closed is not False and closed is not True: + kwargs["closed"] = closed + closed = False + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + if kwargs: + if sys.version_info >= (3, 13): + raise TypeError("TypedDict takes no keyword arguments") + warnings.warn( + "The kwargs-based syntax for TypedDict definitions is deprecated " + "in Python 3.11, will be removed in Python 3.13, and may not be " + "understood by third-party type checkers.", + DeprecationWarning, + stacklevel=2, + ) + + ns = {'__annotations__': dict(fields)} + module = _caller() + if module is not None: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = module + + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed) + td.__orig_bases__ = (TypedDict,) + return td + + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + # On 3.8, this would otherwise return True + if hasattr(typing, "TypedDict") and tp is typing.TypedDict: + return False + return isinstance(tp, _TYPEDDICT_TYPES) + + +if hasattr(typing, "assert_type"): + assert_type = typing.assert_type + +else: + def assert_type(val, typ, /): + """Assert (to the type checker) that the value is of the given type. + + When the type checker encounters a call to assert_type(), it + emits an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # ok + assert_type(name, int) # type checker error + + At runtime this returns the first argument unchanged and otherwise + does nothing. + """ + return val + + +if hasattr(typing, "ReadOnly"): # 3.13+ + get_type_hints = typing.get_type_hints +else: # <=3.13 + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, _AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return _types.GenericAlias(t.__origin__, stripped_args) + if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if hasattr(typing, "Annotated"): # 3.9+ + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + else: # 3.8 + hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + +# Python 3.9+ has PEP 593 (Annotated) +if hasattr(typing, 'Annotated'): + Annotated = typing.Annotated + # Not exported and not a public API, but needed for get_origin() and get_args() + # to work. + _AnnotatedAlias = typing._AnnotatedAlias +# 3.8 +else: + class _AnnotatedAlias(typing._GenericAlias, _root=True): + """Runtime representation of an annotated type. + + At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' + with extra annotations. The alias behaves like a normal typing alias, + instantiating is the same as instantiating the underlying type, binding + it to types is also the same. + """ + def __init__(self, origin, metadata): + if isinstance(origin, _AnnotatedAlias): + metadata = origin.__metadata__ + metadata + origin = origin.__origin__ + super().__init__(origin, origin) + self.__metadata__ = metadata + + def copy_with(self, params): + assert len(params) == 1 + new_type = params[0] + return _AnnotatedAlias(new_type, self.__metadata__) + + def __repr__(self): + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") + + def __reduce__(self): + return operator.getitem, ( + Annotated, (self.__origin__, *self.__metadata__) + ) + + def __eq__(self, other): + if not isinstance(other, _AnnotatedAlias): + return NotImplemented + if self.__origin__ != other.__origin__: + return False + return self.__metadata__ == other.__metadata__ + + def __hash__(self): + return hash((self.__origin__, self.__metadata__)) + + class Annotated: + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type (and will be in + the __origin__ field), the remaining arguments are kept as a tuple in + the __extra__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + + __slots__ = () + + def __new__(cls, *args, **kwargs): + raise TypeError("Type Annotated cannot be instantiated.") + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + allowed_special_forms = (ClassVar, Final) + if get_origin(params[0]) in allowed_special_forms: + origin = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + origin = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + f"Cannot subclass {cls.__module__}.Annotated" + ) + +# Python 3.8 has get_origin() and get_args() but those implementations aren't +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +if sys.version_info[:2] >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.8-3.9 +else: + try: + # 3.9+ + from typing import _BaseGenericAlias + except ImportError: + _BaseGenericAlias = typing._GenericAlias + try: + # 3.9+ + from typing import GenericAlias as _typing_GenericAlias + except ImportError: + _typing_GenericAlias = typing._GenericAlias + + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, _AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, _AnnotatedAlias): + return (tp.__origin__, *tp.__metadata__) + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)): + if getattr(tp, "_special", False): + return () + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") +# 3.8 +else: + TypeAlias = _ExtensionsSpecialForm( + 'TypeAlias', + doc="""Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example + above.""" + ) + + +if hasattr(typing, "NoDefault"): + NoDefault = typing.NoDefault +else: + class NoDefaultTypeMeta(type): + def __setattr__(cls, attr, value): + # TypeError is consistent with the behavior of NoneType + raise TypeError( + f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}" + ) + + class NoDefaultType(metaclass=NoDefaultTypeMeta): + """The type of the NoDefault singleton.""" + + __slots__ = () + + def __new__(cls): + return globals().get("NoDefault") or object.__new__(cls) + + def __repr__(self): + return "typing_extensions.NoDefault" + + def __reduce__(self): + return "NoDefault" + + NoDefault = NoDefaultType() + del NoDefaultType, NoDefaultTypeMeta + + +def _set_default(type_param, default): + type_param.has_default = lambda: default is not NoDefault + type_param.__default__ = default + + +def _set_module(typevarlike): + # for pickling: + def_mod = _caller(depth=3) + if def_mod != 'typing_extensions': + typevarlike.__module__ = def_mod + + +class _DefaultMixin: + """Mixin for TypeVarLike defaults.""" + + __slots__ = () + __init__ = _set_default + + +# Classes using this metaclass must provide a _backported_typevarlike ClassVar +class _TypeVarLikeMeta(type): + def __instancecheck__(cls, __instance: Any) -> bool: + return isinstance(__instance, cls._backported_typevarlike) + + +if _PEP_696_IMPLEMENTED: + from typing import TypeVar +else: + # Add default and infer_variance parameters from PEP 696 and 695 + class TypeVar(metaclass=_TypeVarLikeMeta): + """Type variable.""" + + _backported_typevarlike = typing.TypeVar + + def __new__(cls, name, *constraints, bound=None, + covariant=False, contravariant=False, + default=NoDefault, infer_variance=False): + if hasattr(typing, "TypeAliasType"): + # PEP 695 implemented (3.12+), can pass infer_variance to typing.TypeVar + typevar = typing.TypeVar(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant, + infer_variance=infer_variance) + else: + typevar = typing.TypeVar(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant) + if infer_variance and (covariant or contravariant): + raise ValueError("Variance cannot be specified with infer_variance.") + typevar.__infer_variance__ = infer_variance + + _set_default(typevar, default) + _set_module(typevar) + + def _tvar_prepare_subst(alias, args): + if ( + typevar.has_default() + and alias.__parameters__.index(typevar) == len(args) + ): + args += (typevar.__default__,) + return args + + typevar.__typing_prepare_subst__ = _tvar_prepare_subst + return typevar + + def __init_subclass__(cls) -> None: + raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type") + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.8-3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + + +if _PEP_696_IMPLEMENTED: + from typing import ParamSpec + +# 3.10+ +elif hasattr(typing, 'ParamSpec'): + + # Add default parameter - PEP 696 + class ParamSpec(metaclass=_TypeVarLikeMeta): + """Parameter specification.""" + + _backported_typevarlike = typing.ParamSpec + + def __new__(cls, name, *, bound=None, + covariant=False, contravariant=False, + infer_variance=False, default=NoDefault): + if hasattr(typing, "TypeAliasType"): + # PEP 695 implemented, can pass infer_variance to typing.TypeVar + paramspec = typing.ParamSpec(name, bound=bound, + covariant=covariant, + contravariant=contravariant, + infer_variance=infer_variance) + else: + paramspec = typing.ParamSpec(name, bound=bound, + covariant=covariant, + contravariant=contravariant) + paramspec.__infer_variance__ = infer_variance + + _set_default(paramspec, default) + _set_module(paramspec) + + def _paramspec_prepare_subst(alias, args): + params = alias.__parameters__ + i = params.index(paramspec) + if i == len(args) and paramspec.has_default(): + args = [*args, paramspec.__default__] + if i >= len(args): + raise TypeError(f"Too few arguments for {alias}") + # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612. + if len(params) == 1 and not typing._is_param_expr(args[0]): + assert i == 0 + args = (args,) + # Convert lists to tuples to help other libraries cache the results. + elif isinstance(args[i], list): + args = (*args[:i], tuple(args[i]), *args[i + 1:]) + return args + + paramspec.__typing_prepare_subst__ = _paramspec_prepare_subst + return paramspec + + def __init_subclass__(cls) -> None: + raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type") + +# 3.8-3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list, _DefaultMixin): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + infer_variance=False, default=NoDefault): + list.__init__(self, [self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + self.__infer_variance__ = bool(infer_variance) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + _DefaultMixin.__init__(self, default) + + # for pickling: + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__infer_variance__: + prefix = '' + elif self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + +# 3.8-3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + __class__ = typing._GenericAlias + + # Flag in 3.8. + _special = False + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + +# 3.8-3.9 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not isinstance(parameters[-1], ParamSpec): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = tuple(typing._type_check(p, msg) for p in parameters) + return _ConcatenateGenericAlias(self, parameters) + + +# 3.10+ +if hasattr(typing, 'Concatenate'): + Concatenate = typing.Concatenate + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) +# 3.8 +else: + class _ConcatenateForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateForm( + 'Concatenate', + doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """) + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.8 +else: + class _TypeGuardForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeGuard = _TypeGuardForm( + 'TypeGuard', + doc="""Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """) + +# 3.13+ +if hasattr(typing, 'TypeIs'): + TypeIs = typing.TypeIs +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeIs(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeGuard`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.8 +else: + class _TypeIsForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeIs = _TypeIsForm( + 'TypeIs', + doc="""Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeGuard`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """) + + +# Vendored from cpython typing._SpecialFrom +class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +if hasattr(typing, "LiteralString"): # 3.11+ + LiteralString = typing.LiteralString +else: + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Self"): # 3.11+ + Self = typing.Self +else: + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Never"): # 3.11+ + Never = typing.Never +else: + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, 'Required'): # 3.11+ + Required = typing.Required + NotRequired = typing.NotRequired +elif sys.version_info[:2] >= (3, 9): # 3.9-3.10 + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: # 3.8 + class _RequiredForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Required = _RequiredForm( + 'Required', + doc="""A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """) + NotRequired = _RequiredForm( + 'NotRequired', + doc="""A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """) + + +if hasattr(typing, 'ReadOnly'): + ReadOnly = typing.ReadOnly +elif sys.version_info[:2] >= (3, 9): # 3.9-3.12 + @_ExtensionsSpecialForm + def ReadOnly(self, parameters): + """A special typing construct to mark an item of a TypedDict as read-only. + + For example: + + class Movie(TypedDict): + title: ReadOnly[str] + year: int + + def mutate_movie(m: Movie) -> None: + m["year"] = 1992 # allowed + m["title"] = "The Matrix" # typechecker error + + There is no runtime checking for this property. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: # 3.8 + class _ReadOnlyForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + ReadOnly = _ReadOnlyForm( + 'ReadOnly', + doc="""A special typing construct to mark a key of a TypedDict as read-only. + + For example: + + class Movie(TypedDict): + title: ReadOnly[str] + year: int + + def mutate_movie(m: Movie) -> None: + m["year"] = 1992 # allowed + m["title"] = "The Matrix" # typechecker error + + There is no runtime checking for this propery. + """) + + +_UNPACK_DOC = """\ +Type unpack operator. + +The type unpack operator takes the child types from some container type, +such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For +example: + + # For some generic class `Foo`: + Foo[Unpack[tuple[int, str]]] # Equivalent to Foo[int, str] + + Ts = TypeVarTuple('Ts') + # Specifies that `Bar` is generic in an arbitrary number of types. + # (Think of `Ts` as a tuple of an arbitrary number of individual + # `TypeVar`s, which the `Unpack` is 'pulling out' directly into the + # `Generic[]`.) + class Bar(Generic[Unpack[Ts]]): ... + Bar[int] # Valid + Bar[int, str] # Also valid + +From Python 3.11, this can also be done using the `*` operator: + + Foo[*tuple[int, str]] + class Bar(Generic[*Ts]): ... + +The operator can also be used along with a `TypedDict` to annotate +`**kwargs` in a function signature. For instance: + + class Movie(TypedDict): + name: str + year: int + + # This function expects two keyword arguments - *name* of type `str` and + # *year* of type `int`. + def foo(**kwargs: Unpack[Movie]): ... + +Note that there is only some runtime checking of this operator. Not +everything the runtime allows may be accepted by static type checkers. + +For more information, see PEP 646 and PEP 692. +""" + + +if sys.version_info >= (3, 12): # PEP 692 changed the repr of Unpack[] + Unpack = typing.Unpack + + def _is_unpack(obj): + return get_origin(obj) is Unpack + +elif sys.version_info[:2] >= (3, 9): # 3.9+ + class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True): + def __init__(self, getitem): + super().__init__(getitem) + self.__doc__ = _UNPACK_DOC + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @property + def __typing_unpacked_tuple_args__(self): + assert self.__origin__ is Unpack + assert len(self.__args__) == 1 + arg, = self.__args__ + if isinstance(arg, (typing._GenericAlias, _types.GenericAlias)): + if arg.__origin__ is not tuple: + raise TypeError("Unpack[...] must be used with a tuple type") + return arg.__args__ + return None + + @_UnpackSpecialForm + def Unpack(self, parameters): + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +else: # 3.8 + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + + +if _PEP_696_IMPLEMENTED: + from typing import TypeVarTuple + +elif hasattr(typing, "TypeVarTuple"): # 3.11+ + + def _unpack_args(*args): + newargs = [] + for arg in args: + subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) + if subargs is not None and not (subargs and subargs[-1] is ...): + newargs.extend(subargs) + else: + newargs.append(arg) + return newargs + + # Add default parameter - PEP 696 + class TypeVarTuple(metaclass=_TypeVarLikeMeta): + """Type variable tuple.""" + + _backported_typevarlike = typing.TypeVarTuple + + def __new__(cls, name, *, default=NoDefault): + tvt = typing.TypeVarTuple(name) + _set_default(tvt, default) + _set_module(tvt) + + def _typevartuple_prepare_subst(alias, args): + params = alias.__parameters__ + typevartuple_index = params.index(tvt) + for param in params[typevartuple_index + 1:]: + if isinstance(param, TypeVarTuple): + raise TypeError( + f"More than one TypeVarTuple parameter in {alias}" + ) + + alen = len(args) + plen = len(params) + left = typevartuple_index + right = plen - typevartuple_index - 1 + var_tuple_index = None + fillarg = None + for k, arg in enumerate(args): + if not isinstance(arg, type): + subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) + if subargs and len(subargs) == 2 and subargs[-1] is ...: + if var_tuple_index is not None: + raise TypeError( + "More than one unpacked " + "arbitrary-length tuple argument" + ) + var_tuple_index = k + fillarg = subargs[0] + if var_tuple_index is not None: + left = min(left, var_tuple_index) + right = min(right, alen - var_tuple_index - 1) + elif left + right > alen: + raise TypeError(f"Too few arguments for {alias};" + f" actual {alen}, expected at least {plen - 1}") + if left == alen - right and tvt.has_default(): + replacement = _unpack_args(tvt.__default__) + else: + replacement = args[left: alen - right] + + return ( + *args[:left], + *([fillarg] * (typevartuple_index - left)), + replacement, + *([fillarg] * (plen - right - left - typevartuple_index - 1)), + *args[alen - right:], + ) + + tvt.__typing_prepare_subst__ = _typevartuple_prepare_subst + return tvt + + def __init_subclass__(self, *args, **kwds): + raise TypeError("Cannot subclass special typing classes") + +else: # <=3.10 + class TypeVarTuple(_DefaultMixin): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* + type such as ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, default=NoDefault): + self.__name__ = name + _DefaultMixin.__init__(self, default) + + # for pickling: + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + +if hasattr(typing, "reveal_type"): # 3.11+ + reveal_type = typing.reveal_type +else: # <=3.10 + def reveal_type(obj: T, /) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr) + return obj + + +if hasattr(typing, "_ASSERT_NEVER_REPR_MAX_LENGTH"): # 3.11+ + _ASSERT_NEVER_REPR_MAX_LENGTH = typing._ASSERT_NEVER_REPR_MAX_LENGTH +else: # <=3.10 + _ASSERT_NEVER_REPR_MAX_LENGTH = 100 + + +if hasattr(typing, "assert_never"): # 3.11+ + assert_never = typing.assert_never +else: # <=3.10 + def assert_never(arg: Never, /) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + value = repr(arg) + if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH: + value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...' + raise AssertionError(f"Expected code to be unreachable, but got: {value}") + + +if sys.version_info >= (3, 12): # 3.12+ + # dataclass_transform exists in 3.11 but lacks the frozen_default parameter + dataclass_transform = typing.dataclass_transform +else: # <=3.11 + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + frozen_default: bool = False, + field_specifiers: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + **kwargs: typing.Any, + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``frozen_default`` indicates whether the ``frozen`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_specifiers`` specifies a static list of supported classes + or functions that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "frozen_default": frozen_default, + "field_specifiers": field_specifiers, + "kwargs": kwargs, + } + return cls_or_fn + return decorator + + +if hasattr(typing, "override"): # 3.12+ + override = typing.override +else: # <=3.11 + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(arg: _F, /) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + There is no runtime checking of these properties. The decorator + sets the ``__override__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + + See PEP 698 for details. + + """ + try: + arg.__override__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return arg + + +if hasattr(warnings, "deprecated"): + deprecated = warnings.deprecated +else: + _T = typing.TypeVar("_T") + + class deprecated: + """Indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, the type checker + will generate a diagnostic on usage of the deprecated object. + + Usage: + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + The deprecation message passed to the decorator is saved in the + ``__deprecated__`` attribute on the decorated object. + If applied to an overload, the decorator + must be after the ``@overload`` decorator for the attribute to + exist on the overload as returned by ``get_overloads()``. + + See PEP 702 for details. + + """ + def __init__( + self, + message: str, + /, + *, + category: typing.Optional[typing.Type[Warning]] = DeprecationWarning, + stacklevel: int = 1, + ) -> None: + if not isinstance(message, str): + raise TypeError( + "Expected an object of type str for 'message', not " + f"{type(message).__name__!r}" + ) + self.message = message + self.category = category + self.stacklevel = stacklevel + + def __call__(self, arg: _T, /) -> _T: + # Make sure the inner functions created below don't + # retain a reference to self. + msg = self.message + category = self.category + stacklevel = self.stacklevel + if category is None: + arg.__deprecated__ = msg + return arg + elif isinstance(arg, type): + import functools + from types import MethodType + + original_new = arg.__new__ + + @functools.wraps(original_new) + def __new__(cls, *args, **kwargs): + if cls is arg: + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + if original_new is not object.__new__: + return original_new(cls, *args, **kwargs) + # Mirrors a similar check in object.__new__. + elif cls.__init__ is object.__init__ and (args or kwargs): + raise TypeError(f"{cls.__name__}() takes no arguments") + else: + return original_new(cls) + + arg.__new__ = staticmethod(__new__) + + original_init_subclass = arg.__init_subclass__ + # We need slightly different behavior if __init_subclass__ + # is a bound method (likely if it was implemented in Python) + if isinstance(original_init_subclass, MethodType): + original_init_subclass = original_init_subclass.__func__ + + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = classmethod(__init_subclass__) + # Or otherwise, which likely means it's a builtin such as + # object's implementation of __init_subclass__. + else: + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = __init_subclass__ + + arg.__deprecated__ = __new__.__deprecated__ = msg + __init_subclass__.__deprecated__ = msg + return arg + elif callable(arg): + import functools + + @functools.wraps(arg) + def wrapper(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return arg(*args, **kwargs) + + arg.__deprecated__ = wrapper.__deprecated__ = msg + return wrapper + else: + raise TypeError( + "@deprecated decorator with non-None category must be applied to " + f"a class or callable, not {arg!r}" + ) + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + expect_val = elen + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + + # deal with TypeVarLike defaults + # required TypeVarLikes cannot appear after a defaulted one. + if alen < elen: + # since we validate TypeVarLike default in _collect_type_vars + # or _collect_parameters we can safely check parameters[alen] + if ( + getattr(parameters[alen], '__default__', NoDefault) + is not NoDefault + ): + return + + num_default_tv = sum(getattr(p, '__default__', NoDefault) + is not NoDefault for p in parameters) + + elen -= num_default_tv + + expect_val = f"at least {elen}" + + things = "arguments" if sys.version_info >= (3, 10) else "parameters" + raise TypeError(f"Too {'many' if alen > elen else 'few'} {things}" + f" for {cls}; actual {alen}, expected {expect_val}") +else: + # Python 3.11+ + + def _check_generic(cls, parameters, elen): + """Check correct count for parameters of a generic cls (internal helper). + + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + alen = len(parameters) + if alen != elen: + expect_val = elen + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + + # deal with TypeVarLike defaults + # required TypeVarLikes cannot appear after a defaulted one. + if alen < elen: + # since we validate TypeVarLike default in _collect_type_vars + # or _collect_parameters we can safely check parameters[alen] + if ( + getattr(parameters[alen], '__default__', NoDefault) + is not NoDefault + ): + return + + num_default_tv = sum(getattr(p, '__default__', NoDefault) + is not NoDefault for p in parameters) + + elen -= num_default_tv + + expect_val = f"at least {elen}" + + raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments" + f" for {cls}; actual {alen}, expected {expect_val}") + +if not _PEP_696_IMPLEMENTED: + typing._check_generic = _check_generic + + +def _has_generic_or_protocol_as_origin() -> bool: + try: + frame = sys._getframe(2) + # - Catch AttributeError: not all Python implementations have sys._getframe() + # - Catch ValueError: maybe we're called from an unexpected module + # and the call stack isn't deep enough + except (AttributeError, ValueError): + return False # err on the side of leniency + else: + # If we somehow get invoked from outside typing.py, + # also err on the side of leniency + if frame.f_globals.get("__name__") != "typing": + return False + origin = frame.f_locals.get("origin") + # Cannot use "in" because origin may be an object with a buggy __eq__ that + # throws an error. + return origin is typing.Generic or origin is Protocol or origin is typing.Protocol + + +_TYPEVARTUPLE_TYPES = {TypeVarTuple, getattr(typing, "TypeVarTuple", None)} + + +def _is_unpacked_typevartuple(x) -> bool: + if get_origin(x) is not Unpack: + return False + args = get_args(x) + return ( + bool(args) + and len(args) == 1 + and type(args[0]) in _TYPEVARTUPLE_TYPES + ) + + +# Python 3.11+ _collect_type_vars was renamed to _collect_parameters +if hasattr(typing, '_collect_type_vars'): + def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + + # A required TypeVarLike cannot appear after a TypeVarLike with a default + # if it was a direct call to `Generic[]` or `Protocol[]` + enforce_default_ordering = _has_generic_or_protocol_as_origin() + default_encountered = False + + # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple + type_var_tuple_encountered = False + + for t in types: + if _is_unpacked_typevartuple(t): + type_var_tuple_encountered = True + elif isinstance(t, typevar_types) and t not in tvars: + if enforce_default_ordering: + has_default = getattr(t, '__default__', NoDefault) is not NoDefault + if has_default: + if type_var_tuple_encountered: + raise TypeError('Type parameter with a default' + ' follows TypeVarTuple') + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + typing._collect_type_vars = _collect_type_vars +else: + def _collect_parameters(args): + """Collect all type variables and parameter specifications in args + in order of first appearance (lexicographic order). + + For example:: + + assert _collect_parameters((T, Callable[P, T])) == (T, P) + """ + parameters = [] + + # A required TypeVarLike cannot appear after a TypeVarLike with default + # if it was a direct call to `Generic[]` or `Protocol[]` + enforce_default_ordering = _has_generic_or_protocol_as_origin() + default_encountered = False + + # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple + type_var_tuple_encountered = False + + for t in args: + if isinstance(t, type): + # We don't want __parameters__ descriptor of a bare Python class. + pass + elif isinstance(t, tuple): + # `t` might be a tuple, when `ParamSpec` is substituted with + # `[T, int]`, or `[int, *Ts]`, etc. + for x in t: + for collected in _collect_parameters([x]): + if collected not in parameters: + parameters.append(collected) + elif hasattr(t, '__typing_subst__'): + if t not in parameters: + if enforce_default_ordering: + has_default = ( + getattr(t, '__default__', NoDefault) is not NoDefault + ) + + if type_var_tuple_encountered and has_default: + raise TypeError('Type parameter with a default' + ' follows TypeVarTuple') + + if has_default: + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + + parameters.append(t) + else: + if _is_unpacked_typevartuple(t): + type_var_tuple_encountered = True + for x in getattr(t, '__parameters__', ()): + if x not in parameters: + parameters.append(x) + + return tuple(parameters) + + if not _PEP_696_IMPLEMENTED: + typing._collect_parameters = _collect_parameters + +# Backport typing.NamedTuple as it exists in Python 3.13. +# In 3.11, the ability to define generic `NamedTuple`s was supported. +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. +# On 3.12, we added __orig_bases__ to call-based NamedTuples +# On 3.13, we deprecated kwargs-based NamedTuples +if sys.version_info >= (3, 13): + NamedTuple = typing.NamedTuple +else: + def _make_nmtuple(name, types, module, defaults=()): + fields = [n for n, t in types] + annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") + for n, t in types} + nm_tpl = collections.namedtuple(name, fields, + defaults=defaults, module=module) + nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations + # The `_field_types` attribute was removed in 3.9; + # in earlier versions, it is the same as the `__annotations__` attribute + if sys.version_info < (3, 9): + nm_tpl._field_types = annotations + return nm_tpl + + _prohibited_namedtuple_fields = typing._prohibited + _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) + + class _NamedTupleMeta(type): + def __new__(cls, typename, bases, ns): + assert _NamedTuple in bases + for base in bases: + if base is not _NamedTuple and base is not typing.Generic: + raise TypeError( + 'can only inherit from a NamedTuple type and Generic') + bases = tuple(tuple if base is _NamedTuple else base for base in bases) + if "__annotations__" in ns: + types = ns["__annotations__"] + elif "__annotate__" in ns: + # TODO: Use inspect.VALUE here, and make the annotations lazily evaluated + types = ns["__annotate__"](1) + else: + types = {} + default_names = [] + for field_name in types: + if field_name in ns: + default_names.append(field_name) + elif default_names: + raise TypeError(f"Non-default namedtuple field {field_name} " + f"cannot follow default field" + f"{'s' if len(default_names) > 1 else ''} " + f"{', '.join(default_names)}") + nm_tpl = _make_nmtuple( + typename, types.items(), + defaults=[ns[n] for n in default_names], + module=ns['__module__'] + ) + nm_tpl.__bases__ = bases + if typing.Generic in bases: + if hasattr(typing, '_generic_class_getitem'): # 3.12+ + nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem) + else: + class_getitem = typing.Generic.__class_getitem__.__func__ + nm_tpl.__class_getitem__ = classmethod(class_getitem) + # update from user namespace without overriding special namedtuple attributes + for key, val in ns.items(): + if key in _prohibited_namedtuple_fields: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special_namedtuple_fields: + if key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + try: + set_name = type(val).__set_name__ + except AttributeError: + pass + else: + try: + set_name(val, nm_tpl, key) + except BaseException as e: + msg = ( + f"Error calling __set_name__ on {type(val).__name__!r} " + f"instance {key!r} in {typename!r}" + ) + # BaseException.add_note() existed on py311, + # but the __set_name__ machinery didn't start + # using add_note() until py312. + # Making sure exceptions are raised in the same way + # as in "normal" classes seems most important here. + if sys.version_info >= (3, 12): + e.add_note(msg) + raise + else: + raise RuntimeError(msg) from e + + if typing.Generic in bases: + nm_tpl.__init_subclass__() + return nm_tpl + + _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) + + def _namedtuple_mro_entries(bases): + assert NamedTuple in bases + return (_NamedTuple,) + + @_ensure_subclassable(_namedtuple_mro_entries) + def NamedTuple(typename, fields=_marker, /, **kwargs): + """Typed version of namedtuple. + + Usage:: + + class Employee(NamedTuple): + name: str + id: int + + This is equivalent to:: + + Employee = collections.namedtuple('Employee', ['name', 'id']) + + The resulting class has an extra __annotations__ attribute, giving a + dict that maps field names to types. (The field names are also in + the _fields attribute, which is part of the namedtuple API.) + An alternative equivalent functional syntax is also accepted:: + + Employee = NamedTuple('Employee', [('name', str), ('id', int)]) + """ + if fields is _marker: + if kwargs: + deprecated_thing = "Creating NamedTuple classes using keyword arguments" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "Use the class-based or functional syntax instead." + ) + else: + deprecated_thing = "Failing to pass a value for the 'fields' parameter" + example = f"`{typename} = NamedTuple({typename!r}, [])`" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "To create a NamedTuple class with 0 fields " + "using the functional syntax, " + "pass an empty list, e.g. " + ) + example + "." + elif fields is None: + if kwargs: + raise TypeError( + "Cannot pass `None` as the 'fields' parameter " + "and also specify fields using keyword arguments" + ) + else: + deprecated_thing = "Passing `None` as the 'fields' parameter" + example = f"`{typename} = NamedTuple({typename!r}, [])`" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "To create a NamedTuple class with 0 fields " + "using the functional syntax, " + "pass an empty list, e.g. " + ) + example + "." + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + if fields is _marker or fields is None: + warnings.warn( + deprecation_msg.format(name=deprecated_thing, remove="3.15"), + DeprecationWarning, + stacklevel=2, + ) + fields = kwargs.items() + nt = _make_nmtuple(typename, fields, module=_caller()) + nt.__orig_bases__ = (NamedTuple,) + return nt + + +if hasattr(collections.abc, "Buffer"): + Buffer = collections.abc.Buffer +else: + class Buffer(abc.ABC): # noqa: B024 + """Base class for classes that implement the buffer protocol. + + The buffer protocol allows Python objects to expose a low-level + memory buffer interface. Before Python 3.12, it is not possible + to implement the buffer protocol in pure Python code, or even + to check whether a class implements the buffer protocol. In + Python 3.12 and higher, the ``__buffer__`` method allows access + to the buffer protocol from Python code, and the + ``collections.abc.Buffer`` ABC allows checking whether a class + implements the buffer protocol. + + To indicate support for the buffer protocol in earlier versions, + inherit from this ABC, either in a stub file or at runtime, + or use ABC registration. This ABC provides no methods, because + there is no Python-accessible methods shared by pre-3.12 buffer + classes. It is useful primarily for static checks. + + """ + + # As a courtesy, register the most common stdlib buffer classes. + Buffer.register(memoryview) + Buffer.register(bytearray) + Buffer.register(bytes) + + +# Backport of types.get_original_bases, available on 3.12+ in CPython +if hasattr(_types, "get_original_bases"): + get_original_bases = _types.get_original_bases +else: + def get_original_bases(cls, /): + """Return the class's "original" bases prior to modification by `__mro_entries__`. + + Examples:: + + from typing import TypeVar, Generic + from typing_extensions import NamedTuple, TypedDict + + T = TypeVar("T") + class Foo(Generic[T]): ... + class Bar(Foo[int], float): ... + class Baz(list[str]): ... + Eggs = NamedTuple("Eggs", [("a", int), ("b", str)]) + Spam = TypedDict("Spam", {"a": int, "b": str}) + + assert get_original_bases(Bar) == (Foo[int], float) + assert get_original_bases(Baz) == (list[str],) + assert get_original_bases(Eggs) == (NamedTuple,) + assert get_original_bases(Spam) == (TypedDict,) + assert get_original_bases(int) == (object,) + """ + try: + return cls.__dict__.get("__orig_bases__", cls.__bases__) + except AttributeError: + raise TypeError( + f'Expected an instance of type, not {type(cls).__name__!r}' + ) from None + + +# NewType is a class on Python 3.10+, making it pickleable +# The error message for subclassing instances of NewType was improved on 3.11+ +if sys.version_info >= (3, 11): + NewType = typing.NewType +else: + class NewType: + """NewType creates simple unique types with almost zero + runtime overhead. NewType(name, tp) is considered a subtype of tp + by static type checkers. At runtime, NewType(name, tp) returns + a dummy callable that simply returns its argument. Usage:: + UserId = NewType('UserId', int) + def name_by_id(user_id: UserId) -> str: + ... + UserId('user') # Fails type check + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + num = UserId(5) + 1 # type: int + """ + + def __call__(self, obj, /): + return obj + + def __init__(self, name, tp): + self.__qualname__ = name + if '.' in name: + name = name.rpartition('.')[-1] + self.__name__ = name + self.__supertype__ = tp + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __mro_entries__(self, bases): + # We defined __mro_entries__ to get a better error message + # if a user attempts to subclass a NewType instance. bpo-46170 + supercls_name = self.__name__ + + class Dummy: + def __init_subclass__(cls): + subcls_name = cls.__name__ + raise TypeError( + f"Cannot subclass an instance of NewType. " + f"Perhaps you were looking for: " + f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`" + ) + + return (Dummy,) + + def __repr__(self): + return f'{self.__module__}.{self.__qualname__}' + + def __reduce__(self): + return self.__qualname__ + + if sys.version_info >= (3, 10): + # PEP 604 methods + # It doesn't make sense to have these methods on Python <3.10 + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + +if hasattr(typing, "TypeAliasType"): + TypeAliasType = typing.TypeAliasType +else: + def _is_unionable(obj): + """Corresponds to is_unionable() in unionobject.c in CPython.""" + return obj is None or isinstance(obj, ( + type, + _types.GenericAlias, + _types.UnionType, + TypeAliasType, + )) + + class TypeAliasType: + """Create named, parameterized type aliases. + + This provides a backport of the new `type` statement in Python 3.12: + + type ListOrSet[T] = list[T] | set[T] + + is equivalent to: + + T = TypeVar("T") + ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,)) + + The name ListOrSet can then be used as an alias for the type it refers to. + + The type_params argument should contain all the type parameters used + in the value of the type alias. If the alias is not generic, this + argument is omitted. + + Static type checkers should only support type aliases declared using + TypeAliasType that follow these rules: + + - The first argument (the name) must be a string literal. + - The TypeAliasType instance must be immediately assigned to a variable + of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid, + as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)'). + + """ + + def __init__(self, name: str, value, *, type_params=()): + if not isinstance(name, str): + raise TypeError("TypeAliasType name must be a string") + self.__value__ = value + self.__type_params__ = type_params + + parameters = [] + for type_param in type_params: + if isinstance(type_param, TypeVarTuple): + parameters.extend(type_param) + else: + parameters.append(type_param) + self.__parameters__ = tuple(parameters) + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + # Setting this attribute closes the TypeAliasType from further modification + self.__name__ = name + + def __setattr__(self, name: str, value: object, /) -> None: + if hasattr(self, "__name__"): + self._raise_attribute_error(name) + super().__setattr__(name, value) + + def __delattr__(self, name: str, /) -> Never: + self._raise_attribute_error(name) + + def _raise_attribute_error(self, name: str) -> Never: + # Match the Python 3.12 error messages exactly + if name == "__name__": + raise AttributeError("readonly attribute") + elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}: + raise AttributeError( + f"attribute '{name}' of 'typing.TypeAliasType' objects " + "is not writable" + ) + else: + raise AttributeError( + f"'typing.TypeAliasType' object has no attribute '{name}'" + ) + + def __repr__(self) -> str: + return self.__name__ + + def __getitem__(self, parameters): + if not isinstance(parameters, tuple): + parameters = (parameters,) + parameters = [ + typing._type_check( + item, f'Subscripting {self.__name__} requires a type.' + ) + for item in parameters + ] + return typing._GenericAlias(self, tuple(parameters)) + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + "type 'typing_extensions.TypeAliasType' is not an acceptable base type" + ) + + # The presence of this method convinces typing._type_check + # that TypeAliasTypes are types. + def __call__(self): + raise TypeError("Type alias is not callable") + + if sys.version_info >= (3, 10): + def __or__(self, right): + # For forward compatibility with 3.12, reject Unions + # that are not accepted by the built-in Union. + if not _is_unionable(right): + return NotImplemented + return typing.Union[self, right] + + def __ror__(self, left): + if not _is_unionable(left): + return NotImplemented + return typing.Union[left, self] + + +if hasattr(typing, "is_protocol"): + is_protocol = typing.is_protocol + get_protocol_members = typing.get_protocol_members +else: + def is_protocol(tp: type, /) -> bool: + """Return True if the given type is a Protocol. + + Example:: + + >>> from typing_extensions import Protocol, is_protocol + >>> class P(Protocol): + ... def a(self) -> str: ... + ... b: int + >>> is_protocol(P) + True + >>> is_protocol(int) + False + """ + return ( + isinstance(tp, type) + and getattr(tp, '_is_protocol', False) + and tp is not Protocol + and tp is not typing.Protocol + ) + + def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]: + """Return the set of members defined in a Protocol. + + Example:: + + >>> from typing_extensions import Protocol, get_protocol_members + >>> class P(Protocol): + ... def a(self) -> str: ... + ... b: int + >>> get_protocol_members(P) + frozenset({'a', 'b'}) + + Raise a TypeError for arguments that are not Protocols. + """ + if not is_protocol(tp): + raise TypeError(f'{tp!r} is not a Protocol') + if hasattr(tp, '__protocol_attrs__'): + return frozenset(tp.__protocol_attrs__) + return frozenset(_get_protocol_attrs(tp)) + + +if hasattr(typing, "Doc"): + Doc = typing.Doc +else: + class Doc: + """Define the documentation of a type annotation using ``Annotated``, to be + used in class attributes, function and method parameters, return values, + and variables. + + The value should be a positional-only string literal to allow static tools + like editors and documentation generators to use it. + + This complements docstrings. + + The string value passed is available in the attribute ``documentation``. + + Example:: + + >>> from typing_extensions import Annotated, Doc + >>> def hi(to: Annotated[str, Doc("Who to say hi to")]) -> None: ... + """ + def __init__(self, documentation: str, /) -> None: + self.documentation = documentation + + def __repr__(self) -> str: + return f"Doc({self.documentation!r})" + + def __hash__(self) -> int: + return hash(self.documentation) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Doc): + return NotImplemented + return self.documentation == other.documentation + + +_CapsuleType = getattr(_types, "CapsuleType", None) + +if _CapsuleType is None: + try: + import _socket + except ImportError: + pass + else: + _CAPI = getattr(_socket, "CAPI", None) + if _CAPI is not None: + _CapsuleType = type(_CAPI) + +if _CapsuleType is not None: + CapsuleType = _CapsuleType + __all__.append("CapsuleType") + + +# Aliases for items that have always been in typing. +# Explicitly assign these (rather than using `from typing import *` at the top), +# so that we get a CI error if one of these is deleted from typing.py +# in a future version of Python +AbstractSet = typing.AbstractSet +AnyStr = typing.AnyStr +BinaryIO = typing.BinaryIO +Callable = typing.Callable +Collection = typing.Collection +Container = typing.Container +Dict = typing.Dict +ForwardRef = typing.ForwardRef +FrozenSet = typing.FrozenSet +Generic = typing.Generic +Hashable = typing.Hashable +IO = typing.IO +ItemsView = typing.ItemsView +Iterable = typing.Iterable +Iterator = typing.Iterator +KeysView = typing.KeysView +List = typing.List +Mapping = typing.Mapping +MappingView = typing.MappingView +Match = typing.Match +MutableMapping = typing.MutableMapping +MutableSequence = typing.MutableSequence +MutableSet = typing.MutableSet +Optional = typing.Optional +Pattern = typing.Pattern +Reversible = typing.Reversible +Sequence = typing.Sequence +Set = typing.Set +Sized = typing.Sized +TextIO = typing.TextIO +Tuple = typing.Tuple +Union = typing.Union +ValuesView = typing.ValuesView +cast = typing.cast +no_type_check = typing.no_type_check +no_type_check_decorator = typing.no_type_check_decorator diff --git a/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tsconfig.json b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tsconfig.json new file mode 100644 index 00000000..3d02b2ad --- /dev/null +++ b/rag/knowledge-bases/automating-rag-pipeline/rag-pipeline-with-cicd/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "bin", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file