Search by

tmwk / pdf-section-splitter-bundle

tmwk

Bundle desacoplado para Symfony 5.4+ / PHP 8.1+ que segmenta determinísticamente fichas técnicas en PDF en secciones según características tipográficas y genera tablas HTML semánticas.

Package info

bitbucket.org/tmwk/pdf-section-splitter-bundle

Type:symfony-bundle

pkg:composer/tmwk/pdf-section-splitter-bundle

Statistics

Installs: 19

Dependents: 0

Suggesters: 0

v1.0.4 2026-10-29 12:29 UTC

This package is auto-updated.

Last update: 2026-10-29 12:29:57 UTC


README

Bundle reutilizable y desacoplado para Symfony 5.4+ y PHP 8.1+ diseñado para procesar fichas técnicas y documentos en formato PDF, segmentándolos en secciones deterministas según sus características tipográficas (tamaño relativo de fuente, matrices de escala tipográfica y coordenadas espaciales) y convirtiendo bloques tabulares en tablas HTML semánticas (<table>).

100% Determinista en local: No requiere conexión a servicios externos de IA ni APIs de terceros.

Características

  • Segmentación Dinámica e Inteligente: Detecta automáticamente el tamaño tipográfico base (moda estadística) y calcula el umbral de títulos de secciones (base_font_size * font_threshold_ratio).
  • Detección y Renderizado de Tablas HTML: Convierte automáticamente grillas y listas de columnas tabulares en etiquetas HTML semánticas (<table class="table table-bordered table-sm">, <th>, <td>).
  • Preservación Integral de Texto: Mantiene párrafos explicativos anteriores o posteriores a las tablas sin truncamientos ni pérdida de contenido.
  • Filtro de Ruido Tipográfico: Omite automáticamente números de página ("Pág 1 de 3", "Página 1", etc.), membretes repetitivos y notas al pie sin eliminar palabras clave válidas en el cuerpo del texto.
  • Salida Estandarizada en JSON: Estructura limpia y lista para persistencia en base de datos o APIs REST.

Requisitos

  • PHP 8.1 o superior
  • Symfony 5.4, 6.x o 7.x
  • Extensión ext-json y ext-mbstring
  • smalot/pdfparser: ^2.0

Instalación

Instala el bundle en tu proyecto vía Composer:

composer require tmwk/pdf-section-splitter-bundle

Habilitar el Bundle (si no usas Symfony Flex)

Si Symfony Flex no agrega el bundle automáticamente, regístralo en config/bundles.php:

// config/bundles.php

return [
    // ...
    TMWK\PdfSectionSplitterBundle\TMWKPdfSectionSplitterBundle::class => ['all' => true],
];

Configuración Opcional

Puedes configurar el comportamiento en config/packages/tmwk_pdf_section_splitter.yaml:

tmwk_pdf_section_splitter:
    font_threshold_ratio: 1.15 # Multiplicador sobre la fuente base para calificar un texto como título de sección
    use_ai: false              # Si es true y tmwk/openai está instalado, procesa mediante OpenAI

Uso con Inteligencia Artificial (Opcional)

Si tienes instalado el bundle tmwk/openai (TMWK\OpenAIBundle\Service\OpenAIContentGenerator), puedes habilitar el procesamiento por IA globalmente en el YAML (use_ai: true) o a demanda por llamada:

// Procesar usando IA a demanda (tercer parámetro $useAi):
$jsonResult = $this->pdfSectionSplitter->splitToJson($uploadedFile, customThresholdRatio: null, useAi: true);

Uso

Inyección de Dependencias (Recomendado)

Inyecta el servicio TMWK\PdfSectionSplitterBundle\Service\PdfSectionSplitter en tus controladores, comandos de consola o servicios:

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use TMWK\PdfSectionSplitterBundle\Service\PdfSectionSplitter;

class TechnicalSheetController extends AbstractController
{
    public function __construct(
        private readonly PdfSectionSplitter $pdfSectionSplitter
    ) {
    }

    #[Route('/api/sheets/parse', name: 'api_sheets_parse', methods: ['POST'])]
    public function parse(Request $request): JsonResponse
    {
        $uploadedFile = $request->files->get('pdf_file');
        if (!$uploadedFile) {
            return new JsonResponse(['error' => 'No se adjuntó ningún archivo PDF.'], Response::HTTP_BAD_REQUEST);
        }

        // Procesa directamente una instancia de UploadedFile, File o una ruta string absoluta
        $jsonResult = $this->pdfSectionSplitter->splitToJson($uploadedFile);

        return JsonResponse::fromJsonString($jsonResult, Response::HTTP_OK);
    }
}

Uso Directo / Manual

use TMWK\PdfSectionSplitterBundle\Service\PdfSectionSplitter;

$splitter = new PdfSectionSplitter(
    defaultFontThresholdRatio: 1.15
);

// 1. Con ruta local absoluta
$jsonString = $splitter->splitToJson('/ruta/a/mi-ficha-tecnica.pdf');

// 2. Con URL remota (descarga segura en temporal, valida magic bytes %PDF- y limpia automáticamente)
$jsonString = $splitter->splitToJson('https://mi-servidor.com/archivos/ficha-tecnica.pdf');

// 3. Con instancia UploadedFile de Symfony
$jsonString = $splitter->splitToJson($uploadedFile);

$data = json_decode($jsonString, true);

Formato del JSON de Salida

El método splitToJson() devuelve una cadena JSON con la siguiente estructura:

{
    "document_title": "SPRAY GALVANIZADO EN FRIO",
    "used_ai": false,
    "base_font_size": 11,
    "total_sections": 5,
    "sections": [
        {
            "section_title": "Descripción del producto",
            "content": "Imprimante anticorrosivo formulado en base a resinas especiales..."
        },
        {
            "section_title": "Propiedades",
            "content": "<table class=\"table table-bordered table-sm\">\n  <tr>\n    <th>Propiedad</th>\n    <th>Descripción</th>\n  </tr>\n  <tr>\n    <td>Colores</td>\n    <td>Gris galvanizado</td>\n  </tr>\n</table>"
        },
        {
            "section_title": "Preparación de Superficies",
            "content": "Para asegurar una adecuada adherencia, las superficies deben estar limpias...\n\n<table class=\"table table-bordered table-sm\">\n  <tr>\n    <th>Sustrato</th>\n    <th>Preparación</th>\n  </tr>\n  <tr>\n    <td>Acero</td>\n    <td>Chorreado abrasivo Sa 2 ½...</td>\n  </tr>\n</table>"
        }
    ]
}

Licencia

Este proyecto está bajo la licencia MIT.