aiecs 1.0.1__py3-none-any.whl → 1.7.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +13 -16
- aiecs/__main__.py +7 -7
- aiecs/aiecs_client.py +269 -75
- aiecs/application/executors/operation_executor.py +79 -54
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
- aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
- aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
- aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +98 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
- aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
- aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
- aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
- aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
- aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
- aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
- aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
- aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
- aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
- aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +341 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +913 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
- aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
- aiecs/application/knowledge_graph/search/reranker.py +293 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +471 -0
- aiecs/config/__init__.py +20 -5
- aiecs/config/config.py +762 -31
- aiecs/config/graph_config.py +131 -0
- aiecs/config/tool_config.py +399 -0
- aiecs/core/__init__.py +29 -13
- aiecs/core/interface/__init__.py +2 -2
- aiecs/core/interface/execution_interface.py +22 -22
- aiecs/core/interface/storage_interface.py +37 -88
- aiecs/core/registry/__init__.py +31 -0
- aiecs/core/registry/service_registry.py +92 -0
- aiecs/domain/__init__.py +270 -1
- aiecs/domain/agent/__init__.py +191 -0
- aiecs/domain/agent/base_agent.py +3870 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1435 -0
- aiecs/domain/agent/integration/__init__.py +29 -0
- aiecs/domain/agent/integration/context_compressor.py +216 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
- aiecs/domain/agent/integration/protocols.py +281 -0
- aiecs/domain/agent/integration/retry_policy.py +218 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
- aiecs/domain/agent/lifecycle.py +291 -0
- aiecs/domain/agent/llm_agent.py +692 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +1124 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +163 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
- aiecs/domain/agent/models.py +884 -0
- aiecs/domain/agent/observability.py +479 -0
- aiecs/domain/agent/persistence.py +449 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +159 -0
- aiecs/domain/agent/prompts/formatters.py +187 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +253 -0
- aiecs/domain/agent/tool_agent.py +444 -0
- aiecs/domain/agent/tools/__init__.py +15 -0
- aiecs/domain/agent/tools/schema_generator.py +364 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +469 -0
- aiecs/domain/community/analytics.py +432 -0
- aiecs/domain/community/collaborative_workflow.py +648 -0
- aiecs/domain/community/communication_hub.py +634 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +796 -0
- aiecs/domain/community/community_manager.py +803 -0
- aiecs/domain/community/decision_engine.py +849 -0
- aiecs/domain/community/exceptions.py +231 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +234 -0
- aiecs/domain/community/resource_manager.py +461 -0
- aiecs/domain/community/shared_context_manager.py +589 -0
- aiecs/domain/context/__init__.py +40 -10
- aiecs/domain/context/context_engine.py +1910 -0
- aiecs/domain/context/conversation_models.py +87 -53
- aiecs/domain/context/graph_memory.py +582 -0
- aiecs/domain/execution/model.py +12 -4
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +148 -0
- aiecs/domain/knowledge_graph/models/evidence.py +178 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
- aiecs/domain/knowledge_graph/models/path.py +171 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
- aiecs/domain/knowledge_graph/models/query.py +261 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
- aiecs/domain/knowledge_graph/models/relation.py +202 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
- aiecs/domain/task/dsl_processor.py +172 -56
- aiecs/domain/task/model.py +20 -8
- aiecs/domain/task/task_context.py +27 -24
- aiecs/infrastructure/__init__.py +0 -2
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +837 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
- aiecs/infrastructure/graph_storage/cache.py +424 -0
- aiecs/infrastructure/graph_storage/distributed.py +223 -0
- aiecs/infrastructure/graph_storage/error_handling.py +380 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
- aiecs/infrastructure/graph_storage/metrics.py +344 -0
- aiecs/infrastructure/graph_storage/migration.py +400 -0
- aiecs/infrastructure/graph_storage/pagination.py +483 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
- aiecs/infrastructure/graph_storage/postgres.py +1563 -0
- aiecs/infrastructure/graph_storage/property_storage.py +353 -0
- aiecs/infrastructure/graph_storage/protocols.py +76 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
- aiecs/infrastructure/graph_storage/streaming.py +487 -0
- aiecs/infrastructure/graph_storage/tenant.py +412 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
- aiecs/infrastructure/messaging/websocket_manager.py +51 -35
- aiecs/infrastructure/monitoring/__init__.py +22 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
- aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
- aiecs/infrastructure/monitoring/structured_logger.py +3 -7
- aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
- aiecs/infrastructure/persistence/__init__.py +14 -1
- aiecs/infrastructure/persistence/context_engine_client.py +184 -0
- aiecs/infrastructure/persistence/database_manager.py +67 -43
- aiecs/infrastructure/persistence/file_storage.py +180 -103
- aiecs/infrastructure/persistence/redis_client.py +74 -21
- aiecs/llm/__init__.py +73 -25
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
- aiecs/llm/client_factory.py +224 -36
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +324 -0
- aiecs/llm/clients/google_function_calling_mixin.py +457 -0
- aiecs/llm/clients/googleai_client.py +241 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +897 -0
- aiecs/llm/clients/xai_client.py +201 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +272 -0
- aiecs/llm/config/config_validator.py +206 -0
- aiecs/llm/config/model_config.py +143 -0
- aiecs/llm/protocols.py +149 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +89 -0
- aiecs/main.py +140 -121
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/module_checker.py +499 -0
- aiecs/scripts/aid/version_manager.py +235 -0
- aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
- aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
- aiecs/scripts/dependance_check/__init__.py +15 -0
- aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
- aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
- aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
- aiecs/scripts/knowledge_graph/__init__.py +3 -0
- aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
- aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
- aiecs/scripts/tools_develop/README.md +671 -0
- aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
- aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
- aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
- aiecs/scripts/tools_develop/schema_coverage.py +511 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
- aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
- aiecs/scripts/tools_develop/verify_tools.py +352 -0
- aiecs/tasks/__init__.py +0 -1
- aiecs/tasks/worker.py +115 -47
- aiecs/tools/__init__.py +194 -72
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +330 -0
- aiecs/tools/apisource/providers/__init__.py +112 -0
- aiecs/tools/apisource/providers/base.py +671 -0
- aiecs/tools/apisource/providers/census.py +397 -0
- aiecs/tools/apisource/providers/fred.py +535 -0
- aiecs/tools/apisource/providers/newsapi.py +409 -0
- aiecs/tools/apisource/providers/worldbank.py +352 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +363 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
- aiecs/tools/apisource/tool.py +832 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +334 -0
- aiecs/tools/base_tool.py +415 -21
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +607 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
- aiecs/tools/docs/content_insertion_tool.py +1320 -0
- aiecs/tools/docs/document_creator_tool.py +1323 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1011 -0
- aiecs/tools/docs/document_writer_tool.py +1829 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
- aiecs/tools/langchain_adapter.py +300 -138
- aiecs/tools/schema_generator.py +455 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +581 -0
- aiecs/tools/search_tool/cache.py +264 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +224 -0
- aiecs/tools/search_tool/core.py +778 -0
- aiecs/tools/search_tool/deduplicator.py +119 -0
- aiecs/tools/search_tool/error_handler.py +242 -0
- aiecs/tools/search_tool/metrics.py +343 -0
- aiecs/tools/search_tool/rate_limiter.py +172 -0
- aiecs/tools/search_tool/schemas.py +275 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
- aiecs/tools/statistics/data_loader_tool.py +555 -0
- aiecs/tools/statistics/data_profiler_tool.py +638 -0
- aiecs/tools/statistics/data_transformer_tool.py +580 -0
- aiecs/tools/statistics/data_visualizer_tool.py +498 -0
- aiecs/tools/statistics/model_trainer_tool.py +507 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
- aiecs/tools/task_tools/__init__.py +49 -36
- aiecs/tools/task_tools/chart_tool.py +200 -184
- aiecs/tools/task_tools/classfire_tool.py +268 -267
- aiecs/tools/task_tools/image_tool.py +175 -131
- aiecs/tools/task_tools/office_tool.py +226 -146
- aiecs/tools/task_tools/pandas_tool.py +477 -121
- aiecs/tools/task_tools/report_tool.py +390 -142
- aiecs/tools/task_tools/research_tool.py +149 -79
- aiecs/tools/task_tools/scraper_tool.py +339 -145
- aiecs/tools/task_tools/stats_tool.py +448 -209
- aiecs/tools/temp_file_manager.py +26 -24
- aiecs/tools/tool_executor/__init__.py +18 -16
- aiecs/tools/tool_executor/tool_executor.py +364 -52
- aiecs/utils/LLM_output_structor.py +74 -48
- aiecs/utils/__init__.py +14 -3
- aiecs/utils/base_callback.py +0 -3
- aiecs/utils/cache_provider.py +696 -0
- aiecs/utils/execution_utils.py +50 -31
- aiecs/utils/prompt_loader.py +1 -0
- aiecs/utils/token_usage_repository.py +37 -11
- aiecs/ws/socket_server.py +14 -4
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/METADATA +52 -15
- aiecs-1.7.6.dist-info/RECORD +337 -0
- aiecs-1.7.6.dist-info/entry_points.txt +13 -0
- aiecs/config/registry.py +0 -19
- aiecs/domain/context/content_engine.py +0 -982
- aiecs/llm/base_client.py +0 -99
- aiecs/llm/openai_client.py +0 -125
- aiecs/llm/vertex_client.py +0 -186
- aiecs/llm/xai_client.py +0 -184
- aiecs/scripts/dependency_checker.py +0 -857
- aiecs/scripts/quick_dependency_check.py +0 -269
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.1.dist-info/RECORD +0 -90
- aiecs-1.0.1.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Document Layout Tool
|
|
4
|
+
|
|
5
|
+
This tool is responsible for document layout, page formatting, and
|
|
6
|
+
visual presentation of documents across different formats.
|
|
7
|
+
|
|
8
|
+
Key Features:
|
|
9
|
+
1. Page layout management (margins, orientation, size)
|
|
10
|
+
2. Multi-column layouts and text flow
|
|
11
|
+
3. Headers, footers, and page numbering
|
|
12
|
+
4. Section breaks and page breaks
|
|
13
|
+
5. Typography and spacing control
|
|
14
|
+
6. Format-specific layout optimization
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import uuid
|
|
19
|
+
import tempfile
|
|
20
|
+
import logging
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from typing import Dict, Any, List, Optional
|
|
23
|
+
from enum import Enum
|
|
24
|
+
|
|
25
|
+
from pydantic import BaseModel, Field
|
|
26
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
27
|
+
|
|
28
|
+
from aiecs.tools.base_tool import BaseTool
|
|
29
|
+
from aiecs.tools import register_tool
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PageSize(str, Enum):
|
|
33
|
+
"""Standard page sizes"""
|
|
34
|
+
|
|
35
|
+
A4 = "a4"
|
|
36
|
+
A3 = "a3"
|
|
37
|
+
A5 = "a5"
|
|
38
|
+
LETTER = "letter"
|
|
39
|
+
LEGAL = "legal"
|
|
40
|
+
TABLOID = "tabloid"
|
|
41
|
+
CUSTOM = "custom"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PageOrientation(str, Enum):
|
|
45
|
+
"""Page orientations"""
|
|
46
|
+
|
|
47
|
+
PORTRAIT = "portrait"
|
|
48
|
+
LANDSCAPE = "landscape"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LayoutType(str, Enum):
|
|
52
|
+
"""Document layout types"""
|
|
53
|
+
|
|
54
|
+
SINGLE_COLUMN = "single_column"
|
|
55
|
+
TWO_COLUMN = "two_column"
|
|
56
|
+
THREE_COLUMN = "three_column"
|
|
57
|
+
MULTI_COLUMN = "multi_column"
|
|
58
|
+
MAGAZINE = "magazine"
|
|
59
|
+
NEWSPAPER = "newspaper"
|
|
60
|
+
ACADEMIC = "academic"
|
|
61
|
+
CUSTOM = "custom"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AlignmentType(str, Enum):
|
|
65
|
+
"""Text alignment types"""
|
|
66
|
+
|
|
67
|
+
LEFT = "left"
|
|
68
|
+
CENTER = "center"
|
|
69
|
+
RIGHT = "right"
|
|
70
|
+
JUSTIFY = "justify"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class BreakType(str, Enum):
|
|
74
|
+
"""Break types"""
|
|
75
|
+
|
|
76
|
+
PAGE_BREAK = "page_break"
|
|
77
|
+
SECTION_BREAK = "section_break"
|
|
78
|
+
COLUMN_BREAK = "column_break"
|
|
79
|
+
LINE_BREAK = "line_break"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class HeaderFooterPosition(str, Enum):
|
|
83
|
+
"""Header/footer positions"""
|
|
84
|
+
|
|
85
|
+
HEADER_LEFT = "header_left"
|
|
86
|
+
HEADER_CENTER = "header_center"
|
|
87
|
+
HEADER_RIGHT = "header_right"
|
|
88
|
+
FOOTER_LEFT = "footer_left"
|
|
89
|
+
FOOTER_CENTER = "footer_center"
|
|
90
|
+
FOOTER_RIGHT = "footer_right"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DocumentLayoutError(Exception):
|
|
94
|
+
"""Base exception for Document Layout errors"""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LayoutConfigurationError(DocumentLayoutError):
|
|
98
|
+
"""Raised when layout configuration fails"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class PageSetupError(DocumentLayoutError):
|
|
102
|
+
"""Raised when page setup fails"""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@register_tool("document_layout")
|
|
106
|
+
class DocumentLayoutTool(BaseTool):
|
|
107
|
+
"""
|
|
108
|
+
Document Layout Tool for managing document presentation and formatting
|
|
109
|
+
|
|
110
|
+
This tool provides:
|
|
111
|
+
1. Page setup and formatting
|
|
112
|
+
2. Multi-column layouts
|
|
113
|
+
3. Headers, footers, and page numbering
|
|
114
|
+
4. Typography and spacing control
|
|
115
|
+
5. Break management (page, section, column)
|
|
116
|
+
6. Format-specific layout optimization
|
|
117
|
+
|
|
118
|
+
Integrates with:
|
|
119
|
+
- DocumentCreatorTool for initial document setup
|
|
120
|
+
- DocumentWriterTool for content placement
|
|
121
|
+
- ContentInsertionTool for complex content positioning
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
# Configuration schema
|
|
125
|
+
class Config(BaseSettings):
|
|
126
|
+
"""Configuration for the document layout tool
|
|
127
|
+
|
|
128
|
+
Automatically reads from environment variables with DOC_LAYOUT_ prefix.
|
|
129
|
+
Example: DOC_LAYOUT_TEMP_DIR -> temp_dir
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
model_config = SettingsConfigDict(env_prefix="DOC_LAYOUT_")
|
|
133
|
+
|
|
134
|
+
temp_dir: str = Field(
|
|
135
|
+
default=os.path.join(tempfile.gettempdir(), "document_layouts"),
|
|
136
|
+
description="Temporary directory for layout processing",
|
|
137
|
+
)
|
|
138
|
+
default_page_size: str = Field(default="a4", description="Default page size")
|
|
139
|
+
default_orientation: str = Field(default="portrait", description="Default page orientation")
|
|
140
|
+
default_margins: Dict[str, float] = Field(
|
|
141
|
+
default={"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5},
|
|
142
|
+
description="Default page margins in centimeters (top, bottom, left, right)",
|
|
143
|
+
)
|
|
144
|
+
auto_adjust_layout: bool = Field(
|
|
145
|
+
default=True,
|
|
146
|
+
description="Whether to automatically adjust layout for optimal presentation",
|
|
147
|
+
)
|
|
148
|
+
preserve_formatting: bool = Field(
|
|
149
|
+
default=True,
|
|
150
|
+
description="Whether to preserve existing formatting when applying layouts",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def __init__(self, config: Optional[Dict] = None, **kwargs):
|
|
154
|
+
"""Initialize Document Layout Tool with settings
|
|
155
|
+
|
|
156
|
+
Configuration is automatically loaded by BaseTool from:
|
|
157
|
+
1. Explicit config dict (highest priority)
|
|
158
|
+
2. YAML config files (config/tools/document_layout.yaml)
|
|
159
|
+
3. Environment variables (via dotenv from .env files)
|
|
160
|
+
4. Tool defaults (lowest priority)
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
config: Optional configuration overrides
|
|
164
|
+
**kwargs: Additional arguments passed to BaseTool (e.g., tool_name)
|
|
165
|
+
"""
|
|
166
|
+
super().__init__(config, **kwargs)
|
|
167
|
+
|
|
168
|
+
# Configuration is automatically loaded by BaseTool into self._config_obj
|
|
169
|
+
# Access config via self._config_obj (BaseSettings instance)
|
|
170
|
+
self.config = self._config_obj if self._config_obj else self.Config()
|
|
171
|
+
|
|
172
|
+
self.logger = logging.getLogger(__name__)
|
|
173
|
+
|
|
174
|
+
# Initialize directories
|
|
175
|
+
self._init_directories()
|
|
176
|
+
|
|
177
|
+
# Initialize layout presets
|
|
178
|
+
self._init_layout_presets()
|
|
179
|
+
|
|
180
|
+
# Track layout operations
|
|
181
|
+
self._layout_operations: List[Any] = []
|
|
182
|
+
|
|
183
|
+
def _init_directories(self):
|
|
184
|
+
"""Initialize required directories"""
|
|
185
|
+
os.makedirs(self.config.temp_dir, exist_ok=True)
|
|
186
|
+
|
|
187
|
+
def _init_layout_presets(self):
|
|
188
|
+
"""Initialize built-in layout presets"""
|
|
189
|
+
self.layout_presets = {
|
|
190
|
+
"default": self._get_default_layout(),
|
|
191
|
+
"academic_paper": self._get_academic_paper_layout(),
|
|
192
|
+
"business_report": self._get_business_report_layout(),
|
|
193
|
+
"magazine": self._get_magazine_layout(),
|
|
194
|
+
"newspaper": self._get_newspaper_layout(),
|
|
195
|
+
"presentation": self._get_presentation_layout(),
|
|
196
|
+
"technical_doc": self._get_technical_doc_layout(),
|
|
197
|
+
"letter": self._get_letter_layout(),
|
|
198
|
+
"invoice": self._get_invoice_layout(),
|
|
199
|
+
"brochure": self._get_brochure_layout(),
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Schema definitions
|
|
203
|
+
class Set_page_layoutSchema(BaseModel):
|
|
204
|
+
"""Schema for set_page_layout operation"""
|
|
205
|
+
|
|
206
|
+
document_path: str = Field(description="Path to document")
|
|
207
|
+
page_size: PageSize = Field(description="Page size")
|
|
208
|
+
orientation: PageOrientation = Field(description="Page orientation")
|
|
209
|
+
margins: Dict[str, float] = Field(description="Page margins (top, bottom, left, right)")
|
|
210
|
+
layout_preset: Optional[str] = Field(default=None, description="Layout preset name")
|
|
211
|
+
|
|
212
|
+
class Create_multi_column_layoutSchema(BaseModel):
|
|
213
|
+
"""Schema for create_multi_column_layout operation"""
|
|
214
|
+
|
|
215
|
+
document_path: str = Field(description="Path to document")
|
|
216
|
+
num_columns: int = Field(description="Number of columns")
|
|
217
|
+
column_gap: float = Field(default=1.0, description="Gap between columns (cm)")
|
|
218
|
+
column_widths: Optional[List[float]] = Field(default=None, description="Custom column widths")
|
|
219
|
+
balance_columns: bool = Field(default=True, description="Balance column heights")
|
|
220
|
+
|
|
221
|
+
class Setup_headers_footersSchema(BaseModel):
|
|
222
|
+
"""Schema for setup_headers_footers operation"""
|
|
223
|
+
|
|
224
|
+
document_path: str = Field(description="Path to document")
|
|
225
|
+
header_config: Optional[Dict[str, Any]] = Field(default=None, description="Header configuration")
|
|
226
|
+
footer_config: Optional[Dict[str, Any]] = Field(default=None, description="Footer configuration")
|
|
227
|
+
page_numbering: bool = Field(default=True, description="Include page numbering")
|
|
228
|
+
numbering_style: str = Field(default="numeric", description="Page numbering style")
|
|
229
|
+
|
|
230
|
+
class Insert_breakSchema(BaseModel):
|
|
231
|
+
"""Schema for insert_break operation"""
|
|
232
|
+
|
|
233
|
+
document_path: str = Field(description="Path to document")
|
|
234
|
+
break_type: BreakType = Field(description="Type of break to insert")
|
|
235
|
+
position: Optional[Dict[str, Any]] = Field(default=None, description="Position to insert break")
|
|
236
|
+
break_options: Optional[Dict[str, Any]] = Field(default=None, description="Break-specific options")
|
|
237
|
+
|
|
238
|
+
class Configure_typographySchema(BaseModel):
|
|
239
|
+
"""Schema for configure_typography operation"""
|
|
240
|
+
|
|
241
|
+
document_path: str = Field(description="Path to document")
|
|
242
|
+
font_config: Dict[str, Any] = Field(description="Font configuration")
|
|
243
|
+
spacing_config: Optional[Dict[str, Any]] = Field(default=None, description="Spacing configuration")
|
|
244
|
+
alignment: Optional[AlignmentType] = Field(default=None, description="Text alignment")
|
|
245
|
+
|
|
246
|
+
class Optimize_layout_for_contentSchema(BaseModel):
|
|
247
|
+
"""Schema for optimize_layout_for_content operation"""
|
|
248
|
+
|
|
249
|
+
document_path: str = Field(description="Path to document")
|
|
250
|
+
content_analysis: Dict[str, Any] = Field(description="Analysis of document content")
|
|
251
|
+
optimization_goals: List[str] = Field(description="List of optimization goals")
|
|
252
|
+
|
|
253
|
+
def set_page_layout(
|
|
254
|
+
self,
|
|
255
|
+
document_path: str,
|
|
256
|
+
page_size: PageSize,
|
|
257
|
+
orientation: PageOrientation,
|
|
258
|
+
margins: Dict[str, float],
|
|
259
|
+
layout_preset: Optional[str] = None,
|
|
260
|
+
) -> Dict[str, Any]:
|
|
261
|
+
"""
|
|
262
|
+
Set page layout configuration for document
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
document_path: Path to document
|
|
266
|
+
page_size: Page size (A4, Letter, etc.)
|
|
267
|
+
orientation: Page orientation (portrait/landscape)
|
|
268
|
+
margins: Page margins in cm (top, bottom, left, right)
|
|
269
|
+
layout_preset: Optional layout preset to apply
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Dict containing layout configuration results
|
|
273
|
+
"""
|
|
274
|
+
try:
|
|
275
|
+
start_time = datetime.now()
|
|
276
|
+
operation_id = str(uuid.uuid4())
|
|
277
|
+
|
|
278
|
+
self.logger.info(f"Setting page layout {operation_id} for: {document_path}")
|
|
279
|
+
|
|
280
|
+
# Validate margins
|
|
281
|
+
required_margins = ["top", "bottom", "left", "right"]
|
|
282
|
+
for margin in required_margins:
|
|
283
|
+
if margin not in margins:
|
|
284
|
+
raise LayoutConfigurationError(f"Missing margin: {margin}")
|
|
285
|
+
|
|
286
|
+
# Apply layout preset if specified
|
|
287
|
+
if layout_preset:
|
|
288
|
+
preset_config = self._get_layout_preset(layout_preset)
|
|
289
|
+
if preset_config:
|
|
290
|
+
page_size = preset_config.get("page_size", page_size)
|
|
291
|
+
orientation = preset_config.get("orientation", orientation)
|
|
292
|
+
margins.update(preset_config.get("margins", {}))
|
|
293
|
+
|
|
294
|
+
# Create layout configuration
|
|
295
|
+
layout_config = {
|
|
296
|
+
"page_size": page_size,
|
|
297
|
+
"orientation": orientation,
|
|
298
|
+
"margins": margins,
|
|
299
|
+
"layout_preset": layout_preset,
|
|
300
|
+
"dimensions": self._calculate_page_dimensions(page_size, orientation, margins),
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# Apply layout to document
|
|
304
|
+
self._apply_page_layout_to_document(document_path, layout_config)
|
|
305
|
+
|
|
306
|
+
# Track operation
|
|
307
|
+
operation_info = {
|
|
308
|
+
"operation_id": operation_id,
|
|
309
|
+
"operation_type": "set_page_layout",
|
|
310
|
+
"document_path": document_path,
|
|
311
|
+
"layout_config": layout_config,
|
|
312
|
+
"timestamp": start_time.isoformat(),
|
|
313
|
+
"duration": (datetime.now() - start_time).total_seconds(),
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
self._layout_operations.append(operation_info)
|
|
317
|
+
|
|
318
|
+
self.logger.info(f"Page layout {operation_id} applied successfully")
|
|
319
|
+
return operation_info
|
|
320
|
+
|
|
321
|
+
except Exception as e:
|
|
322
|
+
raise PageSetupError(f"Failed to set page layout: {str(e)}")
|
|
323
|
+
|
|
324
|
+
def create_multi_column_layout(
|
|
325
|
+
self,
|
|
326
|
+
document_path: str,
|
|
327
|
+
num_columns: int,
|
|
328
|
+
column_gap: float = 1.0,
|
|
329
|
+
column_widths: Optional[List[float]] = None,
|
|
330
|
+
balance_columns: bool = True,
|
|
331
|
+
) -> Dict[str, Any]:
|
|
332
|
+
"""
|
|
333
|
+
Create multi-column layout for document
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
document_path: Path to document
|
|
337
|
+
num_columns: Number of columns
|
|
338
|
+
column_gap: Gap between columns in cm
|
|
339
|
+
column_widths: Custom column widths (if None, equal widths)
|
|
340
|
+
balance_columns: Whether to balance column heights
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Dict containing multi-column layout results
|
|
344
|
+
"""
|
|
345
|
+
try:
|
|
346
|
+
start_time = datetime.now()
|
|
347
|
+
operation_id = str(uuid.uuid4())
|
|
348
|
+
|
|
349
|
+
self.logger.info(f"Creating {num_columns}-column layout {operation_id} for: {document_path}")
|
|
350
|
+
|
|
351
|
+
# Validate parameters
|
|
352
|
+
if num_columns < 1:
|
|
353
|
+
raise LayoutConfigurationError("Number of columns must be at least 1")
|
|
354
|
+
if column_widths and len(column_widths) != num_columns:
|
|
355
|
+
raise LayoutConfigurationError("Column widths count must match number of columns")
|
|
356
|
+
|
|
357
|
+
# Calculate column configuration
|
|
358
|
+
column_config = self._calculate_column_configuration(num_columns, column_gap, column_widths, balance_columns)
|
|
359
|
+
|
|
360
|
+
# Apply multi-column layout
|
|
361
|
+
self._apply_multi_column_layout(document_path, column_config)
|
|
362
|
+
|
|
363
|
+
operation_info = {
|
|
364
|
+
"operation_id": operation_id,
|
|
365
|
+
"operation_type": "create_multi_column_layout",
|
|
366
|
+
"document_path": document_path,
|
|
367
|
+
"column_config": column_config,
|
|
368
|
+
"timestamp": start_time.isoformat(),
|
|
369
|
+
"duration": (datetime.now() - start_time).total_seconds(),
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
self._layout_operations.append(operation_info)
|
|
373
|
+
|
|
374
|
+
self.logger.info(f"Multi-column layout {operation_id} created successfully")
|
|
375
|
+
return operation_info
|
|
376
|
+
|
|
377
|
+
except Exception as e:
|
|
378
|
+
raise LayoutConfigurationError(f"Failed to create multi-column layout: {str(e)}")
|
|
379
|
+
|
|
380
|
+
def setup_headers_footers(
|
|
381
|
+
self,
|
|
382
|
+
document_path: str,
|
|
383
|
+
header_config: Optional[Dict[str, Any]] = None,
|
|
384
|
+
footer_config: Optional[Dict[str, Any]] = None,
|
|
385
|
+
page_numbering: bool = True,
|
|
386
|
+
numbering_style: str = "numeric",
|
|
387
|
+
) -> Dict[str, Any]:
|
|
388
|
+
"""
|
|
389
|
+
Setup headers and footers for document
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
document_path: Path to document
|
|
393
|
+
header_config: Header configuration
|
|
394
|
+
footer_config: Footer configuration
|
|
395
|
+
page_numbering: Include page numbering
|
|
396
|
+
numbering_style: Page numbering style (numeric, roman, alpha)
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Dict containing header/footer setup results
|
|
400
|
+
"""
|
|
401
|
+
try:
|
|
402
|
+
start_time = datetime.now()
|
|
403
|
+
operation_id = str(uuid.uuid4())
|
|
404
|
+
|
|
405
|
+
self.logger.info(f"Setting up headers/footers {operation_id} for: {document_path}")
|
|
406
|
+
|
|
407
|
+
# Process header configuration
|
|
408
|
+
processed_header = self._process_header_footer_config(header_config, "header", page_numbering, numbering_style)
|
|
409
|
+
|
|
410
|
+
# Process footer configuration
|
|
411
|
+
processed_footer = self._process_header_footer_config(footer_config, "footer", page_numbering, numbering_style)
|
|
412
|
+
|
|
413
|
+
# Apply headers and footers
|
|
414
|
+
self._apply_headers_footers(document_path, processed_header, processed_footer)
|
|
415
|
+
|
|
416
|
+
operation_info = {
|
|
417
|
+
"operation_id": operation_id,
|
|
418
|
+
"operation_type": "setup_headers_footers",
|
|
419
|
+
"document_path": document_path,
|
|
420
|
+
"header_config": processed_header,
|
|
421
|
+
"footer_config": processed_footer,
|
|
422
|
+
"page_numbering": page_numbering,
|
|
423
|
+
"numbering_style": numbering_style,
|
|
424
|
+
"timestamp": start_time.isoformat(),
|
|
425
|
+
"duration": (datetime.now() - start_time).total_seconds(),
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
self._layout_operations.append(operation_info)
|
|
429
|
+
|
|
430
|
+
self.logger.info(f"Headers/footers {operation_id} setup successfully")
|
|
431
|
+
return operation_info
|
|
432
|
+
|
|
433
|
+
except Exception as e:
|
|
434
|
+
raise LayoutConfigurationError(f"Failed to setup headers/footers: {str(e)}")
|
|
435
|
+
|
|
436
|
+
def insert_break(
|
|
437
|
+
self,
|
|
438
|
+
document_path: str,
|
|
439
|
+
break_type: BreakType,
|
|
440
|
+
position: Optional[Dict[str, Any]] = None,
|
|
441
|
+
break_options: Optional[Dict[str, Any]] = None,
|
|
442
|
+
) -> Dict[str, Any]:
|
|
443
|
+
"""
|
|
444
|
+
Insert page, section, or column break
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
document_path: Path to document
|
|
448
|
+
break_type: Type of break to insert
|
|
449
|
+
position: Position to insert break (line, offset, etc.)
|
|
450
|
+
break_options: Break-specific options
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
Dict containing break insertion results
|
|
454
|
+
"""
|
|
455
|
+
try:
|
|
456
|
+
start_time = datetime.now()
|
|
457
|
+
operation_id = str(uuid.uuid4())
|
|
458
|
+
|
|
459
|
+
self.logger.info(f"Inserting {break_type} break {operation_id} in: {document_path}")
|
|
460
|
+
|
|
461
|
+
# Determine break markup based on type and format
|
|
462
|
+
break_markup = self._generate_break_markup(break_type, break_options)
|
|
463
|
+
|
|
464
|
+
# Insert break at specified position
|
|
465
|
+
self._insert_break_at_position(document_path, break_markup, position)
|
|
466
|
+
|
|
467
|
+
operation_info = {
|
|
468
|
+
"operation_id": operation_id,
|
|
469
|
+
"operation_type": "insert_break",
|
|
470
|
+
"document_path": document_path,
|
|
471
|
+
"break_type": break_type,
|
|
472
|
+
"position": position,
|
|
473
|
+
"break_options": break_options,
|
|
474
|
+
"break_markup": break_markup,
|
|
475
|
+
"timestamp": start_time.isoformat(),
|
|
476
|
+
"duration": (datetime.now() - start_time).total_seconds(),
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
self._layout_operations.append(operation_info)
|
|
480
|
+
|
|
481
|
+
self.logger.info(f"Break {operation_id} inserted successfully")
|
|
482
|
+
return operation_info
|
|
483
|
+
|
|
484
|
+
except Exception as e:
|
|
485
|
+
raise LayoutConfigurationError(f"Failed to insert break: {str(e)}")
|
|
486
|
+
|
|
487
|
+
def configure_typography(
|
|
488
|
+
self,
|
|
489
|
+
document_path: str,
|
|
490
|
+
font_config: Dict[str, Any],
|
|
491
|
+
spacing_config: Optional[Dict[str, Any]] = None,
|
|
492
|
+
alignment: Optional[AlignmentType] = None,
|
|
493
|
+
) -> Dict[str, Any]:
|
|
494
|
+
"""
|
|
495
|
+
Configure typography and text formatting
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
document_path: Path to document
|
|
499
|
+
font_config: Font configuration (family, size, weight, etc.)
|
|
500
|
+
spacing_config: Spacing configuration (line height, paragraph spacing)
|
|
501
|
+
alignment: Text alignment
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
Dict containing typography configuration results
|
|
505
|
+
"""
|
|
506
|
+
try:
|
|
507
|
+
start_time = datetime.now()
|
|
508
|
+
operation_id = str(uuid.uuid4())
|
|
509
|
+
|
|
510
|
+
self.logger.info(f"Configuring typography {operation_id} for: {document_path}")
|
|
511
|
+
|
|
512
|
+
# Process typography configuration
|
|
513
|
+
typography_config = self._process_typography_config(font_config, spacing_config, alignment)
|
|
514
|
+
|
|
515
|
+
# Apply typography settings
|
|
516
|
+
self._apply_typography_settings(document_path, typography_config)
|
|
517
|
+
|
|
518
|
+
operation_info = {
|
|
519
|
+
"operation_id": operation_id,
|
|
520
|
+
"operation_type": "configure_typography",
|
|
521
|
+
"document_path": document_path,
|
|
522
|
+
"typography_config": typography_config,
|
|
523
|
+
"timestamp": start_time.isoformat(),
|
|
524
|
+
"duration": (datetime.now() - start_time).total_seconds(),
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
self._layout_operations.append(operation_info)
|
|
528
|
+
|
|
529
|
+
self.logger.info(f"Typography {operation_id} configured successfully")
|
|
530
|
+
return operation_info
|
|
531
|
+
|
|
532
|
+
except Exception as e:
|
|
533
|
+
raise LayoutConfigurationError(f"Failed to configure typography: {str(e)}")
|
|
534
|
+
|
|
535
|
+
def optimize_layout_for_content(
|
|
536
|
+
self,
|
|
537
|
+
document_path: str,
|
|
538
|
+
content_analysis: Dict[str, Any],
|
|
539
|
+
optimization_goals: List[str],
|
|
540
|
+
) -> Dict[str, Any]:
|
|
541
|
+
"""
|
|
542
|
+
Optimize document layout based on content analysis
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
document_path: Path to document
|
|
546
|
+
content_analysis: Analysis of document content
|
|
547
|
+
optimization_goals: List of optimization goals
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
Dict containing layout optimization results
|
|
551
|
+
"""
|
|
552
|
+
try:
|
|
553
|
+
start_time = datetime.now()
|
|
554
|
+
operation_id = str(uuid.uuid4())
|
|
555
|
+
|
|
556
|
+
self.logger.info(f"Optimizing layout {operation_id} for: {document_path}")
|
|
557
|
+
|
|
558
|
+
# Analyze current layout
|
|
559
|
+
current_layout = self._analyze_current_layout(document_path)
|
|
560
|
+
|
|
561
|
+
# Generate optimization recommendations
|
|
562
|
+
optimization_plan = self._generate_optimization_plan(current_layout, content_analysis, optimization_goals)
|
|
563
|
+
|
|
564
|
+
# Apply optimizations
|
|
565
|
+
optimization_results = self._apply_layout_optimizations(document_path, optimization_plan)
|
|
566
|
+
|
|
567
|
+
operation_info = {
|
|
568
|
+
"operation_id": operation_id,
|
|
569
|
+
"operation_type": "optimize_layout_for_content",
|
|
570
|
+
"document_path": document_path,
|
|
571
|
+
"content_analysis": content_analysis,
|
|
572
|
+
"optimization_goals": optimization_goals,
|
|
573
|
+
"optimization_plan": optimization_plan,
|
|
574
|
+
"optimization_results": optimization_results,
|
|
575
|
+
"timestamp": start_time.isoformat(),
|
|
576
|
+
"duration": (datetime.now() - start_time).total_seconds(),
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
self._layout_operations.append(operation_info)
|
|
580
|
+
|
|
581
|
+
self.logger.info(f"Layout optimization {operation_id} completed successfully")
|
|
582
|
+
return operation_info
|
|
583
|
+
|
|
584
|
+
except Exception as e:
|
|
585
|
+
raise LayoutConfigurationError(f"Failed to optimize layout: {str(e)}")
|
|
586
|
+
|
|
587
|
+
def get_layout_presets(self) -> Dict[str, Any]:
|
|
588
|
+
"""
|
|
589
|
+
Get available layout presets
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
Dict containing available layout presets
|
|
593
|
+
"""
|
|
594
|
+
return {
|
|
595
|
+
"presets": list(self.layout_presets.keys()),
|
|
596
|
+
"preset_details": {name: preset.get("description", "") for name, preset in self.layout_presets.items()},
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
def get_layout_operations(self) -> List[Dict[str, Any]]:
|
|
600
|
+
"""
|
|
601
|
+
Get list of layout operations performed
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
List of layout operation information
|
|
605
|
+
"""
|
|
606
|
+
return self._layout_operations.copy()
|
|
607
|
+
|
|
608
|
+
# Layout preset definitions
|
|
609
|
+
def _get_default_layout(self) -> Dict[str, Any]:
|
|
610
|
+
"""Get default layout configuration"""
|
|
611
|
+
return {
|
|
612
|
+
"description": "Standard single-column layout",
|
|
613
|
+
"page_size": PageSize.A4,
|
|
614
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
615
|
+
"margins": {"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5},
|
|
616
|
+
"columns": 1,
|
|
617
|
+
"font": {"family": "Arial", "size": 12},
|
|
618
|
+
"spacing": {"line_height": 1.5, "paragraph_spacing": 6},
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
def _get_academic_paper_layout(self) -> Dict[str, Any]:
|
|
622
|
+
"""Get academic paper layout configuration"""
|
|
623
|
+
return {
|
|
624
|
+
"description": "Academic paper with double spacing",
|
|
625
|
+
"page_size": PageSize.A4,
|
|
626
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
627
|
+
"margins": {"top": 2.5, "bottom": 2.5, "left": 3.0, "right": 2.5},
|
|
628
|
+
"columns": 1,
|
|
629
|
+
"font": {"family": "Times New Roman", "size": 12},
|
|
630
|
+
"spacing": {"line_height": 2.0, "paragraph_spacing": 0},
|
|
631
|
+
"headers_footers": {
|
|
632
|
+
"header_right": "{author_name}",
|
|
633
|
+
"footer_center": "{page_number}",
|
|
634
|
+
},
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
def _get_business_report_layout(self) -> Dict[str, Any]:
|
|
638
|
+
"""Get business report layout configuration"""
|
|
639
|
+
return {
|
|
640
|
+
"description": "Professional business report layout",
|
|
641
|
+
"page_size": PageSize.A4,
|
|
642
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
643
|
+
"margins": {"top": 2.0, "bottom": 2.0, "left": 2.5, "right": 2.5},
|
|
644
|
+
"columns": 1,
|
|
645
|
+
"font": {"family": "Calibri", "size": 11},
|
|
646
|
+
"spacing": {"line_height": 1.15, "paragraph_spacing": 6},
|
|
647
|
+
"headers_footers": {
|
|
648
|
+
"header_left": "{document_title}",
|
|
649
|
+
"header_right": "{date}",
|
|
650
|
+
"footer_center": "Page {page_number} of {total_pages}",
|
|
651
|
+
"footer_right": "{company_name}",
|
|
652
|
+
},
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
def _get_magazine_layout(self) -> Dict[str, Any]:
|
|
656
|
+
"""Get magazine layout configuration"""
|
|
657
|
+
return {
|
|
658
|
+
"description": "Multi-column magazine layout",
|
|
659
|
+
"page_size": PageSize.A4,
|
|
660
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
661
|
+
"margins": {"top": 1.5, "bottom": 1.5, "left": 1.5, "right": 1.5},
|
|
662
|
+
"columns": 2,
|
|
663
|
+
"column_gap": 0.8,
|
|
664
|
+
"font": {"family": "Georgia", "size": 10},
|
|
665
|
+
"spacing": {"line_height": 1.3, "paragraph_spacing": 4},
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
def _get_newspaper_layout(self) -> Dict[str, Any]:
|
|
669
|
+
"""Get newspaper layout configuration"""
|
|
670
|
+
return {
|
|
671
|
+
"description": "Multi-column newspaper layout",
|
|
672
|
+
"page_size": PageSize.TABLOID,
|
|
673
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
674
|
+
"margins": {"top": 1.0, "bottom": 1.0, "left": 1.0, "right": 1.0},
|
|
675
|
+
"columns": 4,
|
|
676
|
+
"column_gap": 0.5,
|
|
677
|
+
"font": {"family": "Arial", "size": 9},
|
|
678
|
+
"spacing": {"line_height": 1.2, "paragraph_spacing": 3},
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
def _get_presentation_layout(self) -> Dict[str, Any]:
|
|
682
|
+
"""Get presentation layout configuration"""
|
|
683
|
+
return {
|
|
684
|
+
"description": "Landscape presentation layout",
|
|
685
|
+
"page_size": PageSize.A4,
|
|
686
|
+
"orientation": PageOrientation.LANDSCAPE,
|
|
687
|
+
"margins": {"top": 2.0, "bottom": 2.0, "left": 2.0, "right": 2.0},
|
|
688
|
+
"columns": 1,
|
|
689
|
+
"font": {"family": "Helvetica", "size": 14},
|
|
690
|
+
"spacing": {"line_height": 1.4, "paragraph_spacing": 12},
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
def _get_technical_doc_layout(self) -> Dict[str, Any]:
|
|
694
|
+
"""Get technical documentation layout configuration"""
|
|
695
|
+
return {
|
|
696
|
+
"description": "Technical documentation with wide margins for notes",
|
|
697
|
+
"page_size": PageSize.A4,
|
|
698
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
699
|
+
"margins": {"top": 2.0, "bottom": 2.0, "left": 3.5, "right": 2.0},
|
|
700
|
+
"columns": 1,
|
|
701
|
+
"font": {"family": "Consolas", "size": 10},
|
|
702
|
+
"spacing": {"line_height": 1.4, "paragraph_spacing": 8},
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
def _get_letter_layout(self) -> Dict[str, Any]:
|
|
706
|
+
"""Get letter layout configuration"""
|
|
707
|
+
return {
|
|
708
|
+
"description": "Standard business letter layout",
|
|
709
|
+
"page_size": PageSize.LETTER,
|
|
710
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
711
|
+
"margins": {"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5},
|
|
712
|
+
"columns": 1,
|
|
713
|
+
"font": {"family": "Times New Roman", "size": 12},
|
|
714
|
+
"spacing": {"line_height": 1.0, "paragraph_spacing": 12},
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
def _get_invoice_layout(self) -> Dict[str, Any]:
|
|
718
|
+
"""Get invoice layout configuration"""
|
|
719
|
+
return {
|
|
720
|
+
"description": "Invoice and billing document layout",
|
|
721
|
+
"page_size": PageSize.A4,
|
|
722
|
+
"orientation": PageOrientation.PORTRAIT,
|
|
723
|
+
"margins": {"top": 1.5, "bottom": 1.5, "left": 2.0, "right": 2.0},
|
|
724
|
+
"columns": 1,
|
|
725
|
+
"font": {"family": "Arial", "size": 10},
|
|
726
|
+
"spacing": {"line_height": 1.2, "paragraph_spacing": 4},
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
def _get_brochure_layout(self) -> Dict[str, Any]:
|
|
730
|
+
"""Get brochure layout configuration"""
|
|
731
|
+
return {
|
|
732
|
+
"description": "Tri-fold brochure layout",
|
|
733
|
+
"page_size": PageSize.A4,
|
|
734
|
+
"orientation": PageOrientation.LANDSCAPE,
|
|
735
|
+
"margins": {"top": 1.0, "bottom": 1.0, "left": 1.0, "right": 1.0},
|
|
736
|
+
"columns": 3,
|
|
737
|
+
"column_gap": 0.5,
|
|
738
|
+
"font": {"family": "Verdana", "size": 9},
|
|
739
|
+
"spacing": {"line_height": 1.3, "paragraph_spacing": 6},
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
# Helper methods
|
|
743
|
+
def _get_layout_preset(self, preset_name: str) -> Optional[Dict[str, Any]]:
|
|
744
|
+
"""Get layout preset by name"""
|
|
745
|
+
return self.layout_presets.get(preset_name)
|
|
746
|
+
|
|
747
|
+
def _calculate_page_dimensions(
|
|
748
|
+
self,
|
|
749
|
+
page_size: PageSize,
|
|
750
|
+
orientation: PageOrientation,
|
|
751
|
+
margins: Dict[str, float],
|
|
752
|
+
) -> Dict[str, Any]:
|
|
753
|
+
"""Calculate page dimensions including margins"""
|
|
754
|
+
# Standard page sizes in cm
|
|
755
|
+
page_sizes = {
|
|
756
|
+
PageSize.A4: (21.0, 29.7),
|
|
757
|
+
PageSize.A3: (29.7, 42.0),
|
|
758
|
+
PageSize.A5: (14.8, 21.0),
|
|
759
|
+
PageSize.LETTER: (21.59, 27.94),
|
|
760
|
+
PageSize.LEGAL: (21.59, 35.56),
|
|
761
|
+
PageSize.TABLOID: (27.94, 43.18),
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
width, height = page_sizes.get(page_size, (21.0, 29.7))
|
|
765
|
+
|
|
766
|
+
if orientation == PageOrientation.LANDSCAPE:
|
|
767
|
+
width, height = height, width
|
|
768
|
+
|
|
769
|
+
content_width = width - margins["left"] - margins["right"]
|
|
770
|
+
content_height = height - margins["top"] - margins["bottom"]
|
|
771
|
+
|
|
772
|
+
return {
|
|
773
|
+
"page_width": width,
|
|
774
|
+
"page_height": height,
|
|
775
|
+
"content_width": content_width,
|
|
776
|
+
"content_height": content_height,
|
|
777
|
+
"margins": margins,
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
def _calculate_column_configuration(
|
|
781
|
+
self,
|
|
782
|
+
num_columns: int,
|
|
783
|
+
column_gap: float,
|
|
784
|
+
column_widths: Optional[List[float]],
|
|
785
|
+
balance_columns: bool,
|
|
786
|
+
) -> Dict[str, Any]:
|
|
787
|
+
"""Calculate column configuration"""
|
|
788
|
+
config: Dict[str, Any] = {
|
|
789
|
+
"num_columns": num_columns,
|
|
790
|
+
"column_gap": column_gap,
|
|
791
|
+
"balance_columns": balance_columns,
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if column_widths:
|
|
795
|
+
config["column_widths"] = column_widths
|
|
796
|
+
config["custom_widths"] = True
|
|
797
|
+
else:
|
|
798
|
+
# Equal column widths
|
|
799
|
+
config["custom_widths"] = False
|
|
800
|
+
|
|
801
|
+
return config
|
|
802
|
+
|
|
803
|
+
def _apply_page_layout_to_document(self, document_path: str, layout_config: Dict[str, Any]):
|
|
804
|
+
"""Apply page layout configuration to document"""
|
|
805
|
+
# Detect document format
|
|
806
|
+
file_format = self._detect_document_format(document_path)
|
|
807
|
+
|
|
808
|
+
# Generate layout markup based on format
|
|
809
|
+
if file_format == "markdown":
|
|
810
|
+
layout_markup = self._generate_markdown_layout_markup(layout_config)
|
|
811
|
+
elif file_format == "html":
|
|
812
|
+
layout_markup = self._generate_html_layout_markup(layout_config)
|
|
813
|
+
elif file_format == "latex":
|
|
814
|
+
layout_markup = self._generate_latex_layout_markup(layout_config)
|
|
815
|
+
else:
|
|
816
|
+
layout_markup = self._generate_generic_layout_markup(layout_config)
|
|
817
|
+
|
|
818
|
+
# Insert layout markup into document
|
|
819
|
+
self._insert_layout_markup(document_path, layout_markup, "page_layout")
|
|
820
|
+
|
|
821
|
+
def _apply_multi_column_layout(self, document_path: str, column_config: Dict[str, Any]):
|
|
822
|
+
"""Apply multi-column layout to document"""
|
|
823
|
+
file_format = self._detect_document_format(document_path)
|
|
824
|
+
|
|
825
|
+
if file_format == "html":
|
|
826
|
+
column_markup = self._generate_html_column_markup(column_config)
|
|
827
|
+
elif file_format == "latex":
|
|
828
|
+
column_markup = self._generate_latex_column_markup(column_config)
|
|
829
|
+
else:
|
|
830
|
+
column_markup = self._generate_generic_column_markup(column_config)
|
|
831
|
+
|
|
832
|
+
self._insert_layout_markup(document_path, column_markup, "multi_column")
|
|
833
|
+
|
|
834
|
+
def _apply_headers_footers(
|
|
835
|
+
self,
|
|
836
|
+
document_path: str,
|
|
837
|
+
header_config: Dict[str, Any],
|
|
838
|
+
footer_config: Dict[str, Any],
|
|
839
|
+
):
|
|
840
|
+
"""Apply headers and footers to document"""
|
|
841
|
+
file_format = self._detect_document_format(document_path)
|
|
842
|
+
|
|
843
|
+
header_markup = self._generate_header_footer_markup(header_config, "header", file_format)
|
|
844
|
+
footer_markup = self._generate_header_footer_markup(footer_config, "footer", file_format)
|
|
845
|
+
|
|
846
|
+
self._insert_layout_markup(document_path, header_markup, "headers")
|
|
847
|
+
self._insert_layout_markup(document_path, footer_markup, "footers")
|
|
848
|
+
|
|
849
|
+
def _process_header_footer_config(
|
|
850
|
+
self,
|
|
851
|
+
config: Optional[Dict[str, Any]],
|
|
852
|
+
hf_type: str,
|
|
853
|
+
page_numbering: bool,
|
|
854
|
+
numbering_style: str,
|
|
855
|
+
) -> Dict[str, Any]:
|
|
856
|
+
"""Process header or footer configuration"""
|
|
857
|
+
processed = config.copy() if config else {}
|
|
858
|
+
|
|
859
|
+
# Add page numbering if requested
|
|
860
|
+
if page_numbering:
|
|
861
|
+
numbering_text = self._generate_page_numbering_text(numbering_style)
|
|
862
|
+
if hf_type == "footer" and "center" not in processed:
|
|
863
|
+
processed["center"] = numbering_text
|
|
864
|
+
elif hf_type == "header" and "right" not in processed:
|
|
865
|
+
processed["right"] = numbering_text
|
|
866
|
+
|
|
867
|
+
return processed
|
|
868
|
+
|
|
869
|
+
def _generate_page_numbering_text(self, style: str) -> str:
|
|
870
|
+
"""Generate page numbering text based on style"""
|
|
871
|
+
if style == "roman":
|
|
872
|
+
return "{page_roman}"
|
|
873
|
+
elif style == "alpha":
|
|
874
|
+
return "{page_alpha}"
|
|
875
|
+
elif style == "with_total":
|
|
876
|
+
return "Page {page} of {total_pages}"
|
|
877
|
+
else: # numeric
|
|
878
|
+
return "{page}"
|
|
879
|
+
|
|
880
|
+
def _generate_break_markup(self, break_type: BreakType, options: Optional[Dict[str, Any]]) -> str:
|
|
881
|
+
"""Generate break markup based on type"""
|
|
882
|
+
if break_type == BreakType.PAGE_BREAK:
|
|
883
|
+
return "\n<!-- PAGE BREAK -->\n\\newpage\n"
|
|
884
|
+
elif break_type == BreakType.SECTION_BREAK:
|
|
885
|
+
return "\n<!-- SECTION BREAK -->\n\\clearpage\n"
|
|
886
|
+
elif break_type == BreakType.COLUMN_BREAK:
|
|
887
|
+
return "\n<!-- COLUMN BREAK -->\n\\columnbreak\n"
|
|
888
|
+
elif break_type == BreakType.LINE_BREAK:
|
|
889
|
+
return "\n<!-- LINE BREAK -->\n\\linebreak\n"
|
|
890
|
+
else:
|
|
891
|
+
return "\n"
|
|
892
|
+
|
|
893
|
+
def _insert_break_at_position(
|
|
894
|
+
self,
|
|
895
|
+
document_path: str,
|
|
896
|
+
break_markup: str,
|
|
897
|
+
position: Optional[Dict[str, Any]],
|
|
898
|
+
):
|
|
899
|
+
"""Insert break markup at specified position"""
|
|
900
|
+
try:
|
|
901
|
+
with open(document_path, "r", encoding="utf-8") as f:
|
|
902
|
+
content = f.read()
|
|
903
|
+
|
|
904
|
+
if position:
|
|
905
|
+
if "line" in position:
|
|
906
|
+
lines = content.split("\n")
|
|
907
|
+
line_num = position["line"]
|
|
908
|
+
if 0 <= line_num <= len(lines):
|
|
909
|
+
lines.insert(line_num, break_markup.strip())
|
|
910
|
+
content = "\n".join(lines)
|
|
911
|
+
elif "offset" in position:
|
|
912
|
+
offset = position["offset"]
|
|
913
|
+
content = content[:offset] + break_markup + content[offset:]
|
|
914
|
+
else:
|
|
915
|
+
# Append at end
|
|
916
|
+
content += break_markup
|
|
917
|
+
|
|
918
|
+
with open(document_path, "w", encoding="utf-8") as f:
|
|
919
|
+
f.write(content)
|
|
920
|
+
|
|
921
|
+
except Exception as e:
|
|
922
|
+
raise LayoutConfigurationError(f"Failed to insert break: {str(e)}")
|
|
923
|
+
|
|
924
|
+
def _process_typography_config(
|
|
925
|
+
self,
|
|
926
|
+
font_config: Dict[str, Any],
|
|
927
|
+
spacing_config: Optional[Dict[str, Any]],
|
|
928
|
+
alignment: Optional[AlignmentType],
|
|
929
|
+
) -> Dict[str, Any]:
|
|
930
|
+
"""Process typography configuration"""
|
|
931
|
+
config = {
|
|
932
|
+
"font": font_config,
|
|
933
|
+
"spacing": spacing_config or {},
|
|
934
|
+
"alignment": alignment,
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
# Validate font configuration
|
|
938
|
+
required_font_keys = ["family", "size"]
|
|
939
|
+
for key in required_font_keys:
|
|
940
|
+
if key not in font_config:
|
|
941
|
+
raise LayoutConfigurationError(f"Missing font configuration: {key}")
|
|
942
|
+
|
|
943
|
+
return config
|
|
944
|
+
|
|
945
|
+
def _apply_typography_settings(self, document_path: str, typography_config: Dict[str, Any]):
|
|
946
|
+
"""Apply typography settings to document"""
|
|
947
|
+
file_format = self._detect_document_format(document_path)
|
|
948
|
+
|
|
949
|
+
if file_format == "html":
|
|
950
|
+
typography_markup = self._generate_html_typography_markup(typography_config)
|
|
951
|
+
elif file_format == "latex":
|
|
952
|
+
typography_markup = self._generate_latex_typography_markup(typography_config)
|
|
953
|
+
else:
|
|
954
|
+
typography_markup = self._generate_generic_typography_markup(typography_config)
|
|
955
|
+
|
|
956
|
+
self._insert_layout_markup(document_path, typography_markup, "typography")
|
|
957
|
+
|
|
958
|
+
def _analyze_current_layout(self, document_path: str) -> Dict[str, Any]:
|
|
959
|
+
"""Analyze current document layout"""
|
|
960
|
+
try:
|
|
961
|
+
with open(document_path, "r", encoding="utf-8") as f:
|
|
962
|
+
content = f.read()
|
|
963
|
+
|
|
964
|
+
return {
|
|
965
|
+
"content_length": len(content),
|
|
966
|
+
"line_count": len(content.split("\n")),
|
|
967
|
+
"word_count": len(content.split()),
|
|
968
|
+
"has_headers": "header" in content.lower(),
|
|
969
|
+
"has_columns": "column" in content.lower(),
|
|
970
|
+
"file_format": self._detect_document_format(document_path),
|
|
971
|
+
}
|
|
972
|
+
except Exception:
|
|
973
|
+
return {"error": "Failed to analyze layout"}
|
|
974
|
+
|
|
975
|
+
def _generate_optimization_plan(
|
|
976
|
+
self,
|
|
977
|
+
current_layout: Dict[str, Any],
|
|
978
|
+
content_analysis: Dict[str, Any],
|
|
979
|
+
optimization_goals: List[str],
|
|
980
|
+
) -> Dict[str, Any]:
|
|
981
|
+
"""Generate layout optimization plan"""
|
|
982
|
+
plan: Dict[str, Any] = {
|
|
983
|
+
"optimizations": [],
|
|
984
|
+
"goals": optimization_goals,
|
|
985
|
+
"current_layout": current_layout,
|
|
986
|
+
"content_analysis": content_analysis,
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
# Add optimizations based on goals
|
|
990
|
+
for goal in optimization_goals:
|
|
991
|
+
if goal == "readability":
|
|
992
|
+
plan["optimizations"].append(
|
|
993
|
+
{
|
|
994
|
+
"type": "typography",
|
|
995
|
+
"action": "improve_readability",
|
|
996
|
+
"details": "Increase line height and adjust font size",
|
|
997
|
+
}
|
|
998
|
+
)
|
|
999
|
+
elif goal == "space_efficiency":
|
|
1000
|
+
plan["optimizations"].append(
|
|
1001
|
+
{
|
|
1002
|
+
"type": "layout",
|
|
1003
|
+
"action": "optimize_spacing",
|
|
1004
|
+
"details": "Reduce margins and adjust paragraph spacing",
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
elif goal == "professional":
|
|
1008
|
+
plan["optimizations"].append(
|
|
1009
|
+
{
|
|
1010
|
+
"type": "styling",
|
|
1011
|
+
"action": "apply_professional_style",
|
|
1012
|
+
"details": "Use professional fonts and consistent formatting",
|
|
1013
|
+
}
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
return plan
|
|
1017
|
+
|
|
1018
|
+
def _apply_layout_optimizations(self, document_path: str, optimization_plan: Dict[str, Any]) -> Dict[str, Any]:
|
|
1019
|
+
"""Apply layout optimizations based on plan"""
|
|
1020
|
+
results: Dict[str, Any] = {
|
|
1021
|
+
"optimizations_applied": [],
|
|
1022
|
+
"success_count": 0,
|
|
1023
|
+
"error_count": 0,
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
for optimization in optimization_plan.get("optimizations", []):
|
|
1027
|
+
try:
|
|
1028
|
+
# Apply optimization based on type
|
|
1029
|
+
if optimization["type"] == "typography":
|
|
1030
|
+
self._apply_typography_optimization(document_path, optimization)
|
|
1031
|
+
elif optimization["type"] == "layout":
|
|
1032
|
+
self._apply_layout_optimization(document_path, optimization)
|
|
1033
|
+
elif optimization["type"] == "styling":
|
|
1034
|
+
self._apply_styling_optimization(document_path, optimization)
|
|
1035
|
+
|
|
1036
|
+
results["optimizations_applied"].append(optimization)
|
|
1037
|
+
success_count = results.get("success_count", 0)
|
|
1038
|
+
if isinstance(success_count, (int, float)):
|
|
1039
|
+
results["success_count"] = success_count + 1
|
|
1040
|
+
|
|
1041
|
+
except Exception as e:
|
|
1042
|
+
error_count = results.get("error_count", 0)
|
|
1043
|
+
if isinstance(error_count, (int, float)):
|
|
1044
|
+
results["error_count"] = error_count + 1
|
|
1045
|
+
self.logger.warning(f"Failed to apply optimization {optimization['type']}: {e}")
|
|
1046
|
+
|
|
1047
|
+
return results
|
|
1048
|
+
|
|
1049
|
+
def _apply_typography_optimization(self, document_path: str, optimization: Dict[str, Any]):
|
|
1050
|
+
"""Apply typography optimization"""
|
|
1051
|
+
# Simplified implementation
|
|
1052
|
+
|
|
1053
|
+
def _apply_layout_optimization(self, document_path: str, optimization: Dict[str, Any]):
|
|
1054
|
+
"""Apply layout optimization"""
|
|
1055
|
+
# Simplified implementation
|
|
1056
|
+
|
|
1057
|
+
def _apply_styling_optimization(self, document_path: str, optimization: Dict[str, Any]):
|
|
1058
|
+
"""Apply styling optimization"""
|
|
1059
|
+
# Simplified implementation
|
|
1060
|
+
|
|
1061
|
+
def _detect_document_format(self, document_path: str) -> str:
|
|
1062
|
+
"""Detect document format from file extension"""
|
|
1063
|
+
ext = os.path.splitext(document_path)[1].lower()
|
|
1064
|
+
format_map = {
|
|
1065
|
+
".md": "markdown",
|
|
1066
|
+
".markdown": "markdown",
|
|
1067
|
+
".html": "html",
|
|
1068
|
+
".htm": "html",
|
|
1069
|
+
".tex": "latex",
|
|
1070
|
+
".latex": "latex",
|
|
1071
|
+
".txt": "text",
|
|
1072
|
+
}
|
|
1073
|
+
return format_map.get(ext, "text")
|
|
1074
|
+
|
|
1075
|
+
def _generate_markdown_layout_markup(self, layout_config: Dict[str, Any]) -> str:
|
|
1076
|
+
"""Generate Markdown layout markup"""
|
|
1077
|
+
return f"<!-- Layout: {layout_config['page_size']} {layout_config['orientation']} -->\n"
|
|
1078
|
+
|
|
1079
|
+
def _generate_html_layout_markup(self, layout_config: Dict[str, Any]) -> str:
|
|
1080
|
+
"""Generate HTML layout markup"""
|
|
1081
|
+
margins = layout_config["margins"]
|
|
1082
|
+
return f"""<style>
|
|
1083
|
+
@page {{
|
|
1084
|
+
size: {layout_config['page_size']};
|
|
1085
|
+
margin: {margins['top']}cm {margins['right']}cm {margins['bottom']}cm {margins['left']}cm;
|
|
1086
|
+
}}
|
|
1087
|
+
</style>"""
|
|
1088
|
+
|
|
1089
|
+
def _generate_latex_layout_markup(self, layout_config: Dict[str, Any]) -> str:
|
|
1090
|
+
"""Generate LaTeX layout markup"""
|
|
1091
|
+
margins = layout_config["margins"]
|
|
1092
|
+
return f"""\\usepackage[top={margins['top']}cm,bottom={margins['bottom']}cm,left={margins['left']}cm,right={margins['right']}cm]{{geometry}}
|
|
1093
|
+
\\usepackage[{layout_config['orientation']}]{{geometry}}"""
|
|
1094
|
+
|
|
1095
|
+
def _generate_generic_layout_markup(self, layout_config: Dict[str, Any]) -> str:
|
|
1096
|
+
"""Generate generic layout markup"""
|
|
1097
|
+
return f"# Layout Configuration\nPage: {layout_config['page_size']} {layout_config['orientation']}\n"
|
|
1098
|
+
|
|
1099
|
+
def _generate_html_column_markup(self, column_config: Dict[str, Any]) -> str:
|
|
1100
|
+
"""Generate HTML column markup"""
|
|
1101
|
+
num_cols = column_config["num_columns"]
|
|
1102
|
+
gap = column_config["column_gap"]
|
|
1103
|
+
return f"""<style>
|
|
1104
|
+
.multi-column {{
|
|
1105
|
+
column-count: {num_cols};
|
|
1106
|
+
column-gap: {gap}cm;
|
|
1107
|
+
}}
|
|
1108
|
+
</style>
|
|
1109
|
+
<div class="multi-column">"""
|
|
1110
|
+
|
|
1111
|
+
def _generate_latex_column_markup(self, column_config: Dict[str, Any]) -> str:
|
|
1112
|
+
"""Generate LaTeX column markup"""
|
|
1113
|
+
return f"\\begin{{multicols}}{{{column_config['num_columns']}}}"
|
|
1114
|
+
|
|
1115
|
+
def _generate_generic_column_markup(self, column_config: Dict[str, Any]) -> str:
|
|
1116
|
+
"""Generate generic column markup"""
|
|
1117
|
+
return f"<!-- {column_config['num_columns']} columns -->\n"
|
|
1118
|
+
|
|
1119
|
+
def _generate_header_footer_markup(self, config: Dict[str, Any], hf_type: str, file_format: str) -> str:
|
|
1120
|
+
"""Generate header/footer markup"""
|
|
1121
|
+
if file_format == "html":
|
|
1122
|
+
return f"<!-- {hf_type.upper()}: {config} -->\n"
|
|
1123
|
+
elif file_format == "latex":
|
|
1124
|
+
return f"% {hf_type.upper()}: {config}\n"
|
|
1125
|
+
else:
|
|
1126
|
+
return f"# {hf_type.upper()}: {config}\n"
|
|
1127
|
+
|
|
1128
|
+
def _generate_html_typography_markup(self, typography_config: Dict[str, Any]) -> str:
|
|
1129
|
+
"""Generate HTML typography markup"""
|
|
1130
|
+
font = typography_config["font"]
|
|
1131
|
+
return f"""<style>
|
|
1132
|
+
body {{
|
|
1133
|
+
font-family: '{font['family']}';
|
|
1134
|
+
font-size: {font['size']}pt;
|
|
1135
|
+
}}
|
|
1136
|
+
</style>"""
|
|
1137
|
+
|
|
1138
|
+
def _generate_latex_typography_markup(self, typography_config: Dict[str, Any]) -> str:
|
|
1139
|
+
"""Generate LaTeX typography markup"""
|
|
1140
|
+
font = typography_config["font"]
|
|
1141
|
+
return f"\\usepackage{{fontspec}}\n\\setmainfont{{{font['family']}}}\n"
|
|
1142
|
+
|
|
1143
|
+
def _generate_generic_typography_markup(self, typography_config: Dict[str, Any]) -> str:
|
|
1144
|
+
"""Generate generic typography markup"""
|
|
1145
|
+
return f"# Typography: {typography_config['font']}\n"
|
|
1146
|
+
|
|
1147
|
+
def _insert_layout_markup(self, document_path: str, markup: str, markup_type: str):
|
|
1148
|
+
"""Insert layout markup into document"""
|
|
1149
|
+
try:
|
|
1150
|
+
with open(document_path, "r", encoding="utf-8") as f:
|
|
1151
|
+
content = f.read()
|
|
1152
|
+
|
|
1153
|
+
# Insert at the beginning of document
|
|
1154
|
+
content = markup + "\n" + content
|
|
1155
|
+
|
|
1156
|
+
with open(document_path, "w", encoding="utf-8") as f:
|
|
1157
|
+
f.write(content)
|
|
1158
|
+
|
|
1159
|
+
except Exception as e:
|
|
1160
|
+
raise LayoutConfigurationError(f"Failed to insert {markup_type} markup: {str(e)}")
|