diff --git a/src/components/Holos/PDF/Canvas.vue b/src/components/Holos/PDF/Canvas.vue index 92c32c1..d43e9fc 100644 --- a/src/components/Holos/PDF/Canvas.vue +++ b/src/components/Holos/PDF/Canvas.vue @@ -30,19 +30,32 @@ const dragStart = ref({ x: 0, y: 0 }); const resizeStart = ref({ x: 0, y: 0, width: 0, height: 0 }); const fileInput = ref(null); +/** Referencias adicionales para auto-posicionamiento */ +const controlsRef = ref(null); + +/** Constantes para precisión WYSIWYG */ +const PDF_SCALE_FACTOR = 0.75; // 1px = 0.75 puntos PDF +const CANVAS_DPI = 96; // DPI del canvas +const PDF_DPI = 72; // DPI del PDF + /** Propiedades computadas */ const elementStyles = computed(() => { const baseStyles = { left: `${props.element.x}px`, top: `${props.element.y}px`, width: `${props.element.width || 200}px`, - height: `${props.element.height || 40}px` + height: `${props.element.height || 40}px`, + // Asegurar que la fuente se renderice con la misma métrica que el PDF + fontFamily: 'Helvetica, Arial, sans-serif', + lineHeight: '1.2', + letterSpacing: 'normal' }; // Aplicar estilos de formato para elementos de texto if (props.element.type === 'text' && props.element.formatting) { const formatting = props.element.formatting; + // Aplicar tamaño de fuente con factor de corrección if (formatting.fontSize) { baseStyles.fontSize = `${formatting.fontSize}px`; } @@ -50,6 +63,24 @@ const elementStyles = computed(() => { if (formatting.color) { baseStyles.color = formatting.color; } + + // Alineación del contenedor en la página + if (formatting.containerAlign) { + const pageWidth = 794; // A4 width por defecto + const elementWidth = props.element.width || 200; + + switch (formatting.containerAlign) { + case 'center': + baseStyles.left = `${(pageWidth - elementWidth) / 2}px`; + break; + case 'right': + baseStyles.left = `${pageWidth - elementWidth - 50}px`; // 50px margen + break; + case 'left': + default: + baseStyles.left = `${props.element.x}px`; + } + } } return baseStyles; @@ -106,6 +137,25 @@ const inputStyles = computed(() => { return styles; }); +/** Propiedades computadas para posicionamiento dinámico */ +const controlsPosition = computed(() => { + if (!props.isSelected || isEditing.value) return {}; + + // Si el elemento está muy cerca del borde superior (menos de 50px) + const isNearTop = props.element.y < 50; + + return { + position: isNearTop ? 'bottom' : 'top', + styles: isNearTop ? { + top: '100%', + marginTop: '8px' + } : { + bottom: '100%', + marginBottom: '8px' + } + }; +}); + /** Watchers */ watch(() => props.isSelected, (selected) => { if (selected && isEditing.value) { @@ -356,6 +406,44 @@ const getMaxWidth = () => { const getMaxHeight = () => { return 600; // Máximo general }; + +// Nuevos métodos para alineación +const updateContainerAlign = (align) => { + emit('update', { + id: props.element.id, + formatting: { + ...props.element.formatting, + containerAlign: align + } + }); +}; + +const updateSmartAlign = (align) => { + const pageWidth = 794; // Obtener dinámicamente del viewport + const elementWidth = props.element.width || 200; + let newX = props.element.x; + + switch (align) { + case 'center': + newX = (pageWidth - elementWidth) / 2; + break; + case 'right': + newX = pageWidth - elementWidth - 50; // margen derecho + break; + case 'left': + default: + newX = 50; // margen izquierdo + } + + emit('update', { + id: props.element.id, + x: newX, + formatting: { + ...props.element.formatting, + textAlign: align + } + }); +}; \ No newline at end of file diff --git a/src/components/Holos/PDF/PageSizeSelector.vue b/src/components/Holos/PDF/PageSizeSelector.vue index 1ec725e..169cf6a 100644 --- a/src/components/Holos/PDF/PageSizeSelector.vue +++ b/src/components/Holos/PDF/PageSizeSelector.vue @@ -69,13 +69,13 @@ const selectSize = (size) => {