חזרה לגלריהתמיכה בשפות

יצירת   PDF   בעברית

הוראות למשתמש: כנס ל-Google Fonts (למשל Heebo). לחץ על Download Family. פתח את ה-ZIP. כנס לתיקיית static. בחר את הקובץ Heebo-Regular.ttf (וודא שהוא בערך 50KB-200KB). אל תשתמש בקובץ שנמצא בתיקייה הראשית (זהו Variable Font) ואל תשתמש בקבצים מאתרים שממירים ל-Webfont.

R
Roi Shmuel
טקסט הפרומפט
Thought for 10s
הנה מפרט טכני ופרומפט מלא (Master Prompt) שתוכל לשלוח לכל מפתח או AI. הוא כולל את הקוד המדויק, הסברים על השגיאה (reading 'widths') ואיך פותרים אותה אחת ולתמיד.

📄 מפרט טכני: מערכת הפקת PDF בעברית (ללא תלות ברשת)
תיאור הבעיה: בעת שימוש ב-jspdf עם גופנים בעברית, קריסה עם השגיאה TypeError: Cannot read properties of undefined (reading 'widths') מתרחשת כאשר הספרייה מנסה לחשב רוחב טקסט עבור גופן שלא נטען כראוי. הסיבה: ב-99% מהמקרים, המשתמש מעלה קובץ שסיומת שמו היא .ttf, אך בפועל הוא מקודד כ-WOFF או WOFF2 (פורמט אינטרנטי), או שהוא Variable Font. ספריית jspdf תומכת אך ורק ב-Static TTF תקני.

הפתרון: ולידציה בינארית (Magic Bytes) וניקוי Base64 קפדני.

1. מסד נתונים (Entities)
יש לוודא קיום ישות SystemSetting:

key: מחרוזת ייחודית (report_font_base64).
value: טקסט ארוך (יכיל את מחרוזת ה-Base64 הנקייה).
2. פרומפט לצד לקוח (Frontend Implementation)
קובץ: pages/Analytics.js הנחיה: צור מנגנון העלאה שמנקה את ה-Prefix של ה-Data URL ושומר רק את ה-Payload.

// פונקציה לטיפול בהעלאת גופן
const handleFontUpload = async (e) => {
  const file = e.target.files[0];
  if (!file) return;

  // בדיקת סיומת בסיסית
  if (!file.name.toLowerCase().endsWith('.ttf')) {
    alert('חובה להעלות קובץ עם סיומת .ttf בלבד!');
    return;
  }

  // בדיקת גודל (עד 2MB זה סביר לפונט)
  if (file.size > 2 * 1024 * 1024) {
    alert('הקובץ גדול מדי. נא להעלות גרסת Static TTF רגילה.');
    return;
  }

  const reader = new FileReader();
  reader.onload = async (event) => {
    try {
      // קריטי: הסרת ה-Prefix של ה-Data URL!
      // מחרוזת נראית כך: "data:font/ttf;base64,AAEAAA..."
      // אנחנו חייבים רק את מה שאחרי הפסיק.
      const base64Content = event.target.result.split(',')[1];

      if (!base64Content) throw new Error("הקובץ לא נקרא כראוי");

      // שמירה בשרת
      const key = 'report_font_base64';
      // לוגיקה לשמירה ב-SystemSetting (Update or Create)...
      // ... (Code to save to DB) ...
      
      alert('הגופן נשמר בהצלחה! כעת ניתן להפיק דוחות.');
    } catch (err) {
      console.error(err);
      alert('שגיאה בעיבוד הגופן');
    }
  };
  reader.readAsDataURL(file); // קורא כ-Base64
};
3. פרומפט לצד שרת (Backend Implementation)
קובץ: functions/generatePdfReport.js הנחיה: הפונקציה חייבת לבצע בדיקת Magic Bytes לפני הרישום ל-jsPDF. אם הבדיקה נכשלת, יש לבצע Fallback ל-Helvetica כדי למנוע קריסה.

import { createClientFromRequest } from 'npm:@base44/sdk@0.8.4';
import { jsPDF } from 'npm:jspdf@2.5.1';
import autoTable from 'npm:jspdf-autotable@3.8.2';

Deno.serve(async (req) => {
    try {
        const base44 = createClientFromRequest(req);
        // ... (אימות משתמש ושליפת נתונים) ...

        const doc = new jsPDF();
        let fontName = 'helvetica'; // ברירת מחדל בטוחה

        // פונקציה חכמה לטעינת גופן
        const loadCustomFont = async () => {
            try {
                // 1. שליפה מההגדרות
                const settings = await base44.entities.SystemSetting.list();
                const fontSetting = settings.find(s => s.key === 'report_font_base64');
                
                if (!fontSetting?.value) return false;
                
                const base64 = fontSetting.value;

                // 2. בדיקת Magic Bytes (החלק הקריטי למניעת שגיאת widths)
                // מפענחים את 4 הבתים הראשונים
                const rawHeader = atob(base64.substring(0, 20));
                const magic = (rawHeader.charCodeAt(0) << 24) | 
                              (rawHeader.charCodeAt(1) << 16) | 
                              (rawHeader.charCodeAt(2) << 8)  | 
                              rawHeader.charCodeAt(3);

                // חתימות של קבצים בעייתיים
                if (magic === 0x774F4646) throw new Error('זהו קובץ WOFF (לא נתמך)');
                if (magic === 0x774F4632) throw new Error('זהו קובץ WOFF2 (לא נתמך)');
                
                // חתימות תקינות: TTF (0x00010000) או OTF (0x4F54544F)
                if (magic !== 0x00010000 && magic !== 0x4F54544F) {
                    console.warn(`חתימת קובץ לא מוכרת: 0x${magic.toString(16)}, מנסה בכל זאת...`);
                }

                // 3. רישום הגופן ב-jsPDF
                const filename = 'custom_hebrew.ttf';
                doc.addFileToVFS(filename, base64);
                doc.addFont(filename, 'HebrewFont', 'normal');
                
                // ולידציה אחרונה
                if (doc.existsFileInVFS(filename)) {
                    return 'HebrewFont'; // הצלחה
                }
            } catch (e) {
                console.error('נכשל בטעינת גופן מותאם:', e.message);
            }
            return null; // כישלון - נשתמש ב-Fallback
        };

        // ביצוע הטעינה
        const loadedFont = await loadCustomFont();
        if (loadedFont) {
            fontName = loadedFont;
            doc.setFont(fontName);
        } else {
            console.log('משתמש בגופן גיבוי Helvetica (ללא תמיכה בעברית)');
        }

        // --- יצירת ה-PDF ---
        
        // פונקציית היפוך עברית (Reversal) - רק אם הגופן נטען בהצלחה
        const reverse = (str) => {
            if (!str) return "";
            if (fontName === 'helvetica') return str; // אין טעם להפוך אם אין פונט עברי
            const hasHebrew = /[\u0590-\u05FF]/.test(str);
            return hasHebrew ? str.split('').reverse().join('') : str;
        };

        // הוספת טקסט בטוחה
        const safeText = (txt, x, y, opts) => {
            try {
                doc.text(txt, x, y, opts);
            } catch (e) {
                console.error('Text Render Error:', e); // תופס את שגיאת widths אם עדיין קורית
            }
        };

        // דוגמה לשימוש בטבלה
        autoTable(doc, {
            head: [[reverse('שם הלהקה'), reverse('תאריך')]],
            body: events.map(e => [reverse(e.band_name), e.event_date]),
            styles: { 
                font: fontName, // שימוש בגופן שנבחר
                halign: 'right' 
            },
            // עמודות מספרים (כמו תאריך/סכום) עדיף לעתים להשאיר ב-Helvetica אם הגופן העברי לא כולל ספרות טובות
            columnStyles: {
                1: { font: 'helvetica' } 
            }
        });

        // יצוא...
        const pdfBytes = doc.output('arraybuffer');
        return new Response(pdfBytes, {
            headers: { 'Content-Type': 'application/pdf' }
        });

    } catch (error) {
        return Response.json({ error: error.message }, { status: 500 });
    }
});
4. הקובץ הנכון להורדה
כדי שזה יעבוד, חובה להוריד קובץ תקין.