( ′∀`)σ≡σ☆))Д′)レ(゚∀゚;)ヘ=З=З=Зε≡(ノ´_ゝ`)ノ HEX
HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux mail.thebrand.ai 6.8.0-107-generic #107-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 13 19:51:50 UTC 2026 x86_64
User: www-data (33)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/html/tmpr/../tmpr/..//tmpr/..//tmpr/..//tmpr/../tmpr/../tmpr/../wowZ/node-export/index.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const { Document, Packer, Paragraph, TextRun, ImageRun, FrameAnchorType, HorizontalPositionAlign, VerticalPositionAlign, AlignmentType } = require('docx');

const app = express();
const PORT = 3000;

// Increase limit for base64 images
app.use(bodyParser.json({ limit: '50mb' }));
app.use(cors());

/**
 * Convert pixels to Twips (1/1440 inch)
 * 1 px = 1/96 inch (approx)
 * 1 inch = 1440 twips
 * So 1 px = 15 twips
 */
const pxToTwips = (px) => Math.round(px * 15);

/**
 * Convert pixels to Half-points (1/2 point)
 * 1 px = 0.75 pt
 * 1 pt = 2 half-points
 * So 1 px = 1.5 half-points
 */
const pxToHalfPoints = (px) => Math.round(px * 1.5);

/**
 * Clean hex color (remove #)
 */
const cleanColor = (color) => {
    if (!color || typeof color !== 'string') return "000000";
    if (color.startsWith('#')) return color.substring(1);
    // Handle rgba or named colors if necessary (simplification: default to black if complex)
    if (color.startsWith('rgb')) return "000000"; 
    return color;
};

app.post('/export', async (req, res) => {
    try {
        const { width, height, backgroundDataUrl, objects } = req.body;

        if (!backgroundDataUrl) {
            return res.status(400).send("Missing backgroundDataUrl");
        }

        // Decode background image
        // Data URL format: "data:image/png;base64,iVBOR..."
        const base64Data = backgroundDataUrl.replace(/^data:image\/\w+;base64,/, "");
        const imageBuffer = Buffer.from(base64Data, 'base64');

        // Create paragraphs for text objects
        const children = [];

        // 1. Add Background Image (Floating, Behind Text)
        // Note: In Word, "Behind Text" is achieved by floating the image with low z-index
        // However, docx library handles floating images. 
        // We set the margins of the page to 0 and add the image first.
        
        const bgImageRun = new ImageRun({
            data: imageBuffer,
            transformation: {
                width: width, // pixels
                height: height, // pixels
            },
            floating: {
                horizontalPosition: {
                    offset: 0,
                },
                verticalPosition: {
                    offset: 0,
                },
                behindDocument: true, // This puts it behind text
            },
        });

        children.push(new Paragraph({
            children: [bgImageRun],
        }));

        // 2. Process Text Objects
        if (objects && Array.isArray(objects)) {
            objects.forEach(obj => {
                // Filter for simple text: type 'text', 'textbox' or 'i-text'
                // Skip if rotated (as per requirements)
                if ((obj.type === 'text' || obj.type === 'textbox' || obj.type === 'i-text') && (Math.abs(obj.angle || 0) < 0.1)) {
                    
                    const fontSize = pxToHalfPoints(obj.fontSize * (obj.scaleY || 1));
                    const color = cleanColor(obj.fill);
                    
                    // Fabric coordinates are top-left
                    // Word frames are also absolute
                    const x = pxToTwips(obj.left - 100);
                    const y = pxToTwips(obj.top);
                    const w = pxToTwips(obj.width * (obj.scaleX || 1));
                    const h = pxToTwips(obj.height * (obj.scaleY || 1));

                    // Text Alignment
                    let alignment = AlignmentType.LEFT;
                    if (obj.textAlign === 'center') alignment = AlignmentType.CENTER;
                    if (obj.textAlign === 'right') alignment = AlignmentType.RIGHT;
                    if (obj.textAlign === 'justify') alignment = AlignmentType.JUSTIFIED;
alignment = AlignmentType.LEFT;
                    // Create Paragraph with Frame
                    const p = new Paragraph({
                        children: [
                            new TextRun({
                                text: obj.text,
                                font: obj.fontFamily || "Arial",
                                size: fontSize, // half-points
                                color: color,
                            })
                        ],
                        frame: {
                            type: "absolute",
                            position: {
                                x: x,
                                y: y,
                            },
                            width: w,
                            height: h, // exact height might clip, 'auto' is safer but 'atLeast' is common
                            anchor: {
                                horizontal: FrameAnchorType.PAGE,
                                vertical: FrameAnchorType.PAGE,
                            },
                        },
                        alignment: alignment,
                    });

                    children.push(p);
                }
            });
        }

        // Create Document
        const doc = new Document({
            sections: [{
                properties: {
                    page: {
                        margin: {
                            top: 0,
                            right: 0,
                            bottom: 0,
                            left: 0,
                        },
                        size: {
                            width: pxToTwips(width),
                            height: pxToTwips(height),
                        }
                    },
                },
                children: children,
            }],
        });

        // Generate Buffer
        const b64string = await Packer.toBase64String(doc);
        const buffer = Buffer.from(b64string, 'base64');

        res.setHeader('Content-Disposition', 'attachment; filename=export.docx');
        res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
        res.send(buffer);

    } catch (error) {
        console.error(error);
        res.status(500).send("Error generating document: " + error.message);
    }
});

app.listen(PORT, () => {
    console.log(`Export server running on http://localhost:${PORT}`);
    console.log('Text export shifted 200px right');
});