File size: 5,127 Bytes
f38e71b
 
69f734d
f38e71b
 
 
00d3b13
 
 
 
f38e71b
00d3b13
 
 
 
 
 
 
 
 
69f734d
b514f65
00d3b13
 
 
 
 
 
 
f38e71b
b514f65
00d3b13
 
 
5ad001d
00d3b13
69f734d
 
 
00d3b13
 
 
 
 
 
 
b514f65
00d3b13
 
 
 
 
 
 
 
 
5ad001d
f38e71b
00d3b13
 
 
 
f38e71b
 
69f734d
00d3b13
 
 
69f734d
 
 
00d3b13
 
 
 
 
 
 
 
 
 
 
a8621bc
00d3b13
 
69f734d
00d3b13
 
 
 
 
69f734d
00d3b13
 
 
69f734d
 
00d3b13
 
b514f65
 
69f734d
f38e71b
00d3b13
69f734d
5ad001d
a8621bc
00d3b13
 
 
 
b514f65
69f734d
b514f65
00d3b13
b514f65
00d3b13
69f734d
5ad001d
00d3b13
69f734d
5ad001d
b514f65
 
 
69f734d
00d3b13
 
 
69f734d
 
00d3b13
69f734d
00d3b13
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import fitz  # PyMuPDF
import base64
import logging
from fastapi import FastAPI, File, UploadFile
from fastapi.middleware.cors import CORSMiddleware

# Set up logging for Hugging Face console
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI()

# Enable CORS for Figma Plugin access
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

def safe_f(val, default=0.0):
    """Guards against float(None) errors."""
    if val is None:
        return default
    try:
        return float(val)
    except (TypeError, ValueError):
        return default

def get_figma_color(color):
    """Converts various PDF color formats to Figma 0-1 RGB."""
    if color is None:
        return None
    try:
        # If color is an integer, convert to RGB tuple
        if isinstance(color, int):
            color = fitz.utils.getColor(color)
        
        if not isinstance(color, (list, tuple)):
            return None

        # Handle Grayscale (1 component)
        if len(color) == 1:
            val = safe_f(color[0])
            return {"r": val, "g": val, "b": val}
        
        # Handle RGB/CMYK (take first 3)
        if len(color) >= 3:
            return {
                "r": safe_f(color[0]),
                "g": safe_f(color[1]),
                "b": safe_f(color[2])
            }
    except Exception:
        return None
    return None

@app.get("/")
async def root():
    return {"status": "PDF Converter is Online", "engine": "PyMuPDF"}

@app.post("/convert")
async def convert_pdf(file: UploadFile = File(...)):
    try:
        logger.info(f"Processing: {file.filename}")
        pdf_bytes = await file.read()
        doc = fitz.open(stream=pdf_bytes, filetype="pdf")
        pages_data = []

        for page in doc:
            page_dict = {
                "width": safe_f(page.rect.width),
                "height": safe_f(page.rect.height),
                "elements": []
            }

            # 1. TEXT AND IMAGES (using dict for structured layout)
            raw_dict = page.get_text("dict")
            for block in raw_dict.get("blocks", []):
                # Type 0 is Text
                if block.get("type") == 0:
                    for line in block.get("lines", []):
                        for span in line.get("spans", []):
                            page_dict["elements"].append({
                                "type": "TEXT",
                                "content": span.get("text", ""),
                                "x": safe_f(span["bbox"][0]),
                                "y": safe_f(span["bbox"][1]),
                                "size": safe_f(span.get("size"), 12.0),
                                "color": get_figma_color(span.get("color"))
                            })
                # Type 1 is Image
                elif block.get("type") == 1:
                    page_dict["elements"].append({
                        "type": "IMAGE",
                        "bytes": base64.b64encode(block["image"]).decode("utf-8"),
                        "x": safe_f(block["bbox"][0]),
                        "y": safe_f(block["bbox"][1]),
                        "width": safe_f(block["bbox"][2] - block["bbox"][0]),
                        "height": safe_f(block["bbox"][3] - block["bbox"][1])
                    })

            # 2. VECTOR DRAWINGS (Paths, Lines, Rectangles)
            for path in page.get_drawings():
                d_path = ""
                for item in path.get("items", []):
                    # Line
                    if item[0] == "l":
                        d_path += f"M {safe_f(item[1].x)} {safe_f(item[1].y)} L {safe_f(item[2].x)} {safe_f(item[2].y)} "
                    # Rectangle
                    elif item[0] == "re":
                        r = item[1]
                        d_path += f"M {safe_f(r.x0)} {safe_f(r.y0)} L {safe_f(r.x1)} {safe_f(r.y0)} L {safe_f(r.x1)} {safe_f(r.y1)} L {safe_f(r.x0)} {safe_f(r.y1)} Z "
                    # Curve
                    elif item[0] == "c":
                        d_path += f"M {safe_f(item[1].x)} {safe_f(item[1].y)} C {safe_f(item[2].x)} {safe_f(item[2].y)} {safe_f(item[3].x)} {safe_f(item[3].y)} {safe_f(item[4].x)} {safe_f(item[4].y)} "
                
                if d_path:
                    page_dict["elements"].append({
                        "type": "VECTOR",
                        "path": d_path.strip(),
                        "fill": get_figma_color(path.get("fill")),
                        "stroke": get_figma_color(path.get("color")),
                        "strokeWeight": safe_f(path.get("width"), 1.0)
                    })

            pages_data.append(page_dict)
        
        doc.close()
        return {"pages": pages_data}

    except Exception as e:
        logger.error(f"Error during conversion: {str(e)}")
        return {"error": str(e)}

if __name__ == "__main__":
    import uvicorn
    # Important: Hugging Face uses port 7860
    uvicorn.run(app, host="0.0.0.0", port=7860)