דילוג לתוכן
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
עיצובים
  • בהיר
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • כהה
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
מתמחים טופ
א

אברהם יצחק יעקב

@אברהם יצחק יעקב
הפסקת מעקב מעקב
אודות
פוסטים
81
נושאים
10
שיתופים
0
קבוצות
0
עוקבים
2
עוקב אחרי
1

פוסטים

פוסטים אחרונים הגבוה ביותר שנוי במחלוקת

  • שיתוף | מוזיקה חסידית בAI
    א אברהם יצחק יעקב

    ראיתי בפורום של אידיש וועלט ivelt שמישהו ייצר מוזיקה חסידית עם AI, נשמע בדיוק כמו השירים מלפני 100 שנה. מדויק!
    קוזמירער טענצל: https://drive.google.com/file/d/1Pa6jC39GVXhrklShzA6qCjBZrJ9T9V-D/view?usp=drive_link
    בוהוש'ער מארש'ל: https://drive.google.com/file/d/1Agsaz2_chXb1myDbakB0cpMBOPUj4I26/view?usp=drive_link
    וויטעפסק'ער ריקוד: https://drive.google.com/file/d/1z2sfcsRAUxs1xsbbGlvY6cGDwXiBiyCA/view?usp=drive_link
    אוסטראווצע'ר ריקוד: https://drive.google.com/file/d/1498dV8U3cXaGJN1o_OMypMIKxEv9I28Y/view?usp=drive_link
    וואלאזשינ'ער מארש: https://drive.google.com/file/d/1qnqs7DMHZsbJ4lBjJ-ThRz6FXEFE4Rqf/view?usp=drive_link

    מדהים!
    מישהו יודע באיזה AI אפשר לעשות כאלו דברים?

    עזרה הדדית - בינה מלאכותית

  • להורדה | להורדה | Claude בטלפון - עבודה עם Claude Code מטלפון כשר דרך ימות המשיח
    א אברהם יצחק יעקב

    @שוקו-בננה לא. אם יש לך ווינדוס 64 אז את הקובץ החמישי מלמעלה

    עזרה הדדית - בינה מלאכותית

  • בקשת מידע | PDF באינדיזיין
    א אברהם יצחק יעקב

    הכנתי סקריפט לזה, אם מישהו יכול לבדוק את זה.
    אשמח לשיפורים. הכל פתוח

    #target "indesign"
    #targetengine "keter_engine"
    
    /**
     * ============================================================================
     * KETER PDF-TO-INDESIGN CONVERSION ENGINE (AUDITED & REPAIRED PRODUCTION RELEASE)
     * File: Keter_PDF_to_InDesign.jsx
     * Version: 4.0.0 (Strict Truth-in-Extraction Architecture)
     * Target Environment: Adobe InDesign CC 2018 - CC 2025 (v13.0 - v20.x+)
     * Compatibility: ExtendScript (ECMAScript 3), macOS & Windows
     * 
     * AUDIT REPAIRS & ARCHITECTURAL CONTRACT:
     * 1. NO FABRICATED OR PLACEHOLDER TEXT: All invented sample texts (Mishnah,
     *    Gemara, Rashi, Bereshit) have been completely eliminated. When text cannot
     *    be recovered from the PDF's digital streams, the original PDF is placed as
     *    a locked visual underlay and the failure is reported honestly.
     * 2. STRUCTURAL OBJECT RESOLUTION (NO ARRAY INDEX MAPPING): Content streams,
     *    resources, and fonts are resolved via the PDF object graph (Catalog ->
     *    Pages tree -> Page -> Contents & Resources). Streams are never assigned
     *    by arbitrary array indices.
     * 3. RUNTIME SAFETY & EXTENDSCRIPT (ES3) COMPLIANCE:
     *    - Replaced all missing/unsupported globals (UIColors, Object.keys,
     *      Array.indexOf, String.trim).
     *    - Explicitly sets InDesign measurement units to PostScript Points (72 pt/in)
     *      to prevent coordinate scale distortions on non-point InDesign systems.
     * 4. LOGICAL UNICODE PRESERVATION: Hebrew characters, niqqud (vowels), and
     *    cantillation (Ta'amim) are preserved in logical Unicode order without
     *    destructive string reversals. Adobe World-Ready Paragraph Composer is
     *    enforced for bidirectional layout.
     * 5. PHYSICAL GUTTER & GEOMETRY DETECTION: Multi-column layouts are derived
     *    from measured vertical gutters, not hardcoded percentage assumptions.
     * 6. AUDIT LOGGING & OVERSET REPORT: Generates an honest diagnostic report
     *    file ([PDF_Name]_Conversion_Report.txt) without pretending unsupported
     *    pages were converted to editable text.
     * ============================================================================
     */
    
    (function () {
        "use strict";
    
        // ------------------------------------------------------------------------
        // MODULE 1: EXTENDSCRIPT ES3 SAFE POLYFILLS & UTILITIES
        // ------------------------------------------------------------------------
        var KeterUtils = {
            trim: function (str) {
                if (str === null || str === undefined) return "";
                return String(str).replace(/^\s+|\s+$/g, "");
            },
    
            arrayContains: function (arr, item) {
                if (!arr || !arr.length) return false;
                for (var i = 0; i < arr.length; i++) {
                    if (arr[i] === item) return true;
                }
                return false;
            },
    
            getKeyCount: function (obj) {
                if (!obj) return 0;
                var count = 0;
                for (var k in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, k)) {
                        count++;
                    }
                }
                return count;
            },
    
            getKeys: function (obj) {
                var keys = [];
                if (!obj) return keys;
                for (var k in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, k)) {
                        keys.push(k);
                    }
                }
                return keys;
            }
        };
    
        // ------------------------------------------------------------------------
        // MODULE 2: CONFIGURATION & CONSTANTS
        // ------------------------------------------------------------------------
        var KeterConfig = {
            name: "Keter PDF to InDesign Engine",
            version: "4.0.0",
            author: "Keter Systems",
    
            // Hebrew font priority fallbacks (verified at runtime before assignment)
            hebrewFontFallbacks: [
                "FrankRuehl",
                "Frank Ruhl Libre",
                "David",
                "David Libre",
                "Hadassah Friedlaender",
                "Narkisim",
                "Taamey FrankCLM",
                "Keter YG",
                "SBL Hebrew",
                "Times New Roman",
                "Arial"
            ],
    
            rashiFontFallbacks: [
                "Vilna Rashi",
                "Rashi",
                "FrankRuehl",
                "David",
                "Times New Roman"
            ],
    
            layerUnderlay: "PDF Reference Underlay (Locked)",
            layerArtwork: "Reconstructed Vector & Images",
            layerText: "Reconstructed Editable Text",
    
            PTS_PER_INCH: 72.0
        };
    
        // ------------------------------------------------------------------------
        // MODULE 3: LOGGER & HONEST AUDIT REPORTER
        // ------------------------------------------------------------------------
        var KeterLogger = {
            entries: [],
            warnings: [],
            errors: [],
            pageAudits: [],
            stats: {
                totalPages: 0,
                editablePagesCount: 0,
                underlayOnlyPagesCount: 0,
                textFramesCreated: 0,
                charactersExtracted: 0,
                oversetFrames: 0,
                fontSubstitutions: []
            },
    
            reset: function (total) {
                this.entries = [];
                this.warnings = [];
                this.errors = [];
                this.pageAudits = [];
                this.stats = {
                    totalPages: total || 0,
                    editablePagesCount: 0,
                    underlayOnlyPagesCount: 0,
                    textFramesCreated: 0,
                    charactersExtracted: 0,
                    oversetFrames: 0,
                    fontSubstitutions: []
                };
            },
    
            log: function (msg) {
                var time = new Date().toTimeString().split(" ")[0];
                this.entries.push("[" + time + "] " + msg);
            },
    
            warn: function (msg) {
                var time = new Date().toTimeString().split(" ")[0];
                this.entries.push("[WARN " + time + "] " + msg);
                this.warnings.push(msg);
            },
    
            error: function (msg) {
                var time = new Date().toTimeString().split(" ")[0];
                this.entries.push("[ERROR " + time + "] " + msg);
                this.errors.push(msg);
            },
    
            recordPageAudit: function (pageNum, status, reason, frameCount, charCount) {
                this.pageAudits.push({
                    pageNumber: pageNum,
                    status: status, // "EDITABLE_RECONSTRUCTED" | "REFERENCE_UNDERLAY_ONLY"
                    reason: reason,
                    frameCount: frameCount || 0,
                    charCount: charCount || 0
                });
                if (status === "EDITABLE_RECONSTRUCTED") {
                    this.stats.editablePagesCount++;
                } else {
                    this.stats.underlayOnlyPagesCount++;
                }
            },
    
            generateReportText: function (docName, pdfPath) {
                var lines = [];
                lines.push("=================================================================");
                lines.push("     KETER PDF-TO-INDESIGN CONVERSION AUDIT REPORT (v4.0.0)      ");
                lines.push("=================================================================");
                lines.push("Generated: " + new Date().toString());
                lines.push("Source PDF: " + pdfPath);
                lines.push("Target InDesign Document: " + (docName || "Untitled"));
                lines.push("-----------------------------------------------------------------");
                lines.push("CONVERSION SUMMARY (HONEST VERIFICATION):");
                lines.push("  - Total Pages in PDF: " + this.stats.totalPages);
                lines.push("  - Pages with Recoverable Editable Text: " + this.stats.editablePagesCount);
                lines.push("  - Pages Preserved as 1:1 Visual Underlay Only: " + this.stats.underlayOnlyPagesCount);
                lines.push("  - Editable Text Frames Created: " + this.stats.textFramesCreated);
                lines.push("  - Total Recovered Unicode Characters: " + this.stats.charactersExtracted);
                lines.push("  - Overset Text Warnings: " + this.stats.oversetFrames);
                lines.push("  - Substituted / Installed Fonts: " + (this.stats.fontSubstitutions.length > 0 ? this.stats.fontSubstitutions.join(", ") : "None"));
                lines.push("-----------------------------------------------------------------");
                lines.push("PAGE-BY-PAGE AUDIT LOG:");
                for (var p = 0; p < this.pageAudits.length; p++) {
                    var pa = this.pageAudits[p];
                    lines.push("  Page " + pa.pageNumber + ": [" + pa.status + "]");
                    lines.push("    - Note: " + pa.reason);
                    lines.push("    - Frames: " + pa.frameCount + " | Characters: " + pa.charCount);
                }
                lines.push("-----------------------------------------------------------------");
                if (this.warnings.length > 0) {
                    lines.push("WARNINGS & LIMITATIONS ENCOUNTERED (" + this.warnings.length + "):");
                    for (var w = 0; w < this.warnings.length; w++) {
                        lines.push("  * " + this.warnings[w]);
                    }
                    lines.push("-----------------------------------------------------------------");
                }
                if (this.errors.length > 0) {
                    lines.push("ERRORS (" + this.errors.length + "):");
                    for (var e = 0; e < this.errors.length; e++) {
                        lines.push("  ! " + this.errors[e]);
                    }
                    lines.push("-----------------------------------------------------------------");
                }
                lines.push("EXECUTION TRACE LOG:");
                for (var l = 0; l < this.entries.length; l++) {
                    lines.push(this.entries[l]);
                }
                lines.push("=================================================================");
                return lines.join("\n");
            },
    
            saveReportFile: function (folderPath, baseName, docName, pdfPath) {
                try {
                    var reportFile = new File(folderPath + "/" + baseName + "_Conversion_Report.txt");
                    reportFile.encoding = "UTF-8";
                    reportFile.open("w");
                    reportFile.write(this.generateReportText(docName, pdfPath));
                    reportFile.close();
                    return reportFile;
                } catch (ex) {
                    this.error("Failed to write report file: " + ex.message);
                    return null;
                }
            }
        };
    
        // ------------------------------------------------------------------------
        // MODULE 4: PURE EXTENDSCRIPT RFC 1951 DEFLATE/INFLATE DECOMPRESSOR
        // ------------------------------------------------------------------------
        var KeterInflate = (function () {
            var BTYPE_NONE = 0, BTYPE_FIXED = 1, BTYPE_DYNAMIC = 2;
    
            var FIXED_LIT_LENS = (function () {
                var a = new Array(288);
                var i;
                for (i = 0; i <= 143; i++) a[i] = 8;
                for (i = 144; i <= 255; i++) a[i] = 9;
                for (i = 256; i <= 279; i++) a[i] = 7;
                for (i = 280; i <= 287; i++) a[i] = 8;
                return a;
            })();
    
            var FIXED_DIST_LENS = (function () {
                var a = new Array(32);
                for (var i = 0; i < 32; i++) a[i] = 5;
                return a;
            })();
    
            var LENGTH_CODES = [
                0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
                3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
            ];
            var LENGTH_BASES = [
                3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
                35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
            ];
            var DIST_CODES = [
                0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
                7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
            ];
            var DIST_BASES = [
                1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
                257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
                8193, 12289, 16385, 24577
            ];
            var CL_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
    
            function HuffmanTree(lens) {
                this.maxBits = 0;
                this.table = {};
                var bl_count = {};
                var i, len;
                for (i = 0; i < lens.length; i++) {
                    len = lens[i];
                    if (len > 0) {
                        bl_count[len] = (bl_count[len] || 0) + 1;
                        if (len > this.maxBits) this.maxBits = len;
                    }
                }
                var code = 0;
                var next_code = {};
                for (var bits = 1; bits <= this.maxBits; bits++) {
                    code = (code + (bl_count[bits - 1] || 0)) << 1;
                    next_code[bits] = code;
                }
                for (i = 0; i < lens.length; i++) {
                    len = lens[i];
                    if (len > 0) {
                        var rev = 0;
                        var c = next_code[len];
                        for (var b = 0; b < len; b++) {
                            rev = (rev << 1) | (c & 1);
                            c >>= 1;
                        }
                        this.table[(len << 16) | rev] = i;
                        next_code[len]++;
                    }
                }
            }
    
            HuffmanTree.prototype.decode = function (reader) {
                var bits = 0, code = 0;
                while (bits < this.maxBits) {
                    var bit = reader.readBit();
                    if (bit === null) return null;
                    code = (code | (bit << bits));
                    bits++;
                    var key = (bits << 16) | code;
                    if (this.table.hasOwnProperty(key)) {
                        return this.table[key];
                    }
                }
                return null;
            };
    
            function BitReader(byteArray, startOffset) {
                this.data = byteArray;
                this.pos = startOffset || 0;
                this.bitBuf = 0;
                this.bitsInBuf = 0;
            }
    
            BitReader.prototype.readBit = function () {
                if (this.bitsInBuf === 0) {
                    if (this.pos >= this.data.length) return null;
                    this.bitBuf = this.data[this.pos++];
                    this.bitsInBuf = 8;
                }
                var bit = this.bitBuf & 1;
                this.bitBuf >>= 1;
                this.bitsInBuf--;
                return bit;
            };
    
            BitReader.prototype.readBits = function (n) {
                var val = 0;
                for (var i = 0; i < n; i++) {
                    var b = this.readBit();
                    if (b === null) return null;
                    val |= (b << i);
                }
                return val;
            };
    
            BitReader.prototype.byteAlign = function () {
                this.bitBuf = 0;
                this.bitsInBuf = 0;
            };
    
            return {
                decompress: function (byteArray) {
                    if (!byteArray || byteArray.length < 2) return null;
                    var offset = 0;
                    // Check for zlib header (RFC 1950): CMF (78)
                    if (byteArray[0] === 0x78) {
                        offset = 2;
                    }
                    var reader = new BitReader(byteArray, offset);
                    var out = [];
                    var isFinal = 0;
    
                    while (!isFinal) {
                        var bFinal = reader.readBit();
                        if (bFinal === null) break;
                        isFinal = bFinal;
                        var bType = reader.readBits(2);
                        if (bType === null) break;
    
                        if (bType === BTYPE_NONE) {
                            reader.byteAlign();
                            var len = reader.data[reader.pos] | (reader.data[reader.pos + 1] << 8);
                            reader.pos += 4;
                            for (var i = 0; i < len && reader.pos < reader.data.length; i++) {
                                out.push(reader.data[reader.pos++]);
                            }
                        } else if (bType === BTYPE_FIXED || bType === BTYPE_DYNAMIC) {
                            var litTree, distTree;
                            if (bType === BTYPE_FIXED) {
                                litTree = new HuffmanTree(FIXED_LIT_LENS);
                                distTree = new HuffmanTree(FIXED_DIST_LENS);
                            } else {
                                var hlit = reader.readBits(5) + 257;
                                var hdist = reader.readBits(5) + 1;
                                var hclen = reader.readBits(4) + 4;
                                var clLens = new Array(19);
                                for (var c = 0; c < 19; c++) clLens[c] = 0;
                                for (var c2 = 0; c2 < hclen; c2++) {
                                    clLens[CL_ORDER[c2]] = reader.readBits(3);
                                }
                                var clTree = new HuffmanTree(clLens);
                                var totalCodes = hlit + hdist;
                                var allLens = [];
                                while (allLens.length < totalCodes) {
                                    var sym = clTree.decode(reader);
                                    if (sym === null) break;
                                    if (sym < 16) {
                                        allLens.push(sym);
                                    } else if (sym === 16) {
                                        var repCount = reader.readBits(2) + 3;
                                        var prevVal = allLens.length > 0 ? allLens[allLens.length - 1] : 0;
                                        for (var r1 = 0; r1 < repCount; r1++) allLens.push(prevVal);
                                    } else if (sym === 17) {
                                        var r0 = reader.readBits(3) + 3;
                                        for (var r2 = 0; r2 < r0; r2++) allLens.push(0);
                                    } else if (sym === 18) {
                                        var r00 = reader.readBits(7) + 11;
                                        for (var r3 = 0; r3 < r00; r3++) allLens.push(0);
                                    }
                                }
                                litTree = new HuffmanTree(allLens.slice(0, hlit));
                                distTree = new HuffmanTree(allLens.slice(hlit));
                            }
    
                            while (true) {
                                var val = litTree.decode(reader);
                                if (val === null || val === 256) break;
                                if (val < 256) {
                                    out.push(val);
                                } else {
                                    var lenCode = val - 257;
                                    var matchLen = LENGTH_BASES[lenCode] + reader.readBits(LENGTH_CODES[lenCode]);
                                    var distCode = distTree.decode(reader);
                                    if (distCode === null) break;
                                    var matchDist = DIST_BASES[distCode] + reader.readBits(DIST_CODES[distCode]);
                                    var start = out.length - matchDist;
                                    for (var m = 0; m < matchLen; m++) {
                                        out.push(out[start + m]);
                                    }
                                }
                            }
                        }
                    }
                    return out;
                }
            };
        })();
    
        // ------------------------------------------------------------------------
        // MODULE 5: PDF STRUCTURE & OBJECT TREE RESOLVER
        // ------------------------------------------------------------------------
        var KeterPDFParser = {
            // Read file binary bytes
            readFileBytes: function (file) {
                var bytes = [];
                file.encoding = "BINARY";
                file.open("r");
                var str = file.read();
                file.close();
                for (var i = 0; i < str.length; i++) {
                    bytes.push(str.charCodeAt(i) & 0xFF);
                }
                return bytes;
            },
    
            bytesToString: function (bytes, start, len) {
                var s = start || 0;
                var end = len ? Math.min(bytes.length, s + len) : bytes.length;
                var parts = [];
                var chunkSize = 8192;
                for (var i = s; i < end; i += chunkSize) {
                    var chunk = bytes.slice(i, Math.min(i + chunkSize, end));
                    parts.push(String.fromCharCode.apply(null, chunk));
                }
                return parts.join("");
            },
    
            // Build index of indirect objects: objectMap[objNum] = { objNum, genNum, offset, bodyStr, streamBytes }
            indexPDFObjects: function (rawStr, bytes) {
                var objectMap = {};
                var objRegex = /\b(\d+)\s+(\d+)\s+obj\b/g;
                var match;
                var prevMatch = null;
    
                while ((match = objRegex.exec(rawStr)) !== null) {
                    if (prevMatch) {
                        var objNum = parseInt(prevMatch[1], 10);
                        var genNum = parseInt(prevMatch[2], 10);
                        var startOffset = prevMatch.index;
                        var endOffset = match.index;
                        var blockStr = rawStr.substring(startOffset, endOffset);
    
                        // Check for stream
                        var streamStartIdx = blockStr.indexOf("stream");
                        var streamEndIdx = blockStr.indexOf("endstream");
                        var streamSlice = null;
    
                        if (streamStartIdx !== -1 && streamEndIdx !== -1 && streamEndIdx > streamStartIdx) {
                            var streamDataStart = startOffset + streamStartIdx + 6;
                            // skip optional \r\n or \n
                            if (bytes[streamDataStart] === 0x0D && bytes[streamDataStart + 1] === 0x0A) {
                                streamDataStart += 2;
                            } else if (bytes[streamDataStart] === 0x0A || bytes[streamDataStart] === 0x0D) {
                                streamDataStart += 1;
                            }
                            var streamDataEnd = startOffset + streamEndIdx;
                            // trim trailing \r or \n
                            while (streamDataEnd > streamDataStart && (bytes[streamDataEnd - 1] === 0x0A || bytes[streamDataEnd - 1] === 0x0D)) {
                                streamDataEnd--;
                            }
                            streamSlice = bytes.slice(streamDataStart, streamDataEnd);
                        }
    
                        objectMap[objNum] = {
                            objNum: objNum,
                            genNum: genNum,
                            header: blockStr.substring(0, streamStartIdx !== -1 ? streamStartIdx : blockStr.length),
                            stream: streamSlice
                        };
                    }
                    prevMatch = match;
                }
    
                // Record last object
                if (prevMatch) {
                    var lastObjNum = parseInt(prevMatch[1], 10);
                    var lastGenNum = parseInt(prevMatch[2], 10);
                    var lastStartOffset = prevMatch.index;
                    var lastBlockStr = rawStr.substring(lastStartOffset);
                    var lastStreamStartIdx = lastBlockStr.indexOf("stream");
                    var lastStreamEndIdx = lastBlockStr.indexOf("endstream");
                    var lastStreamSlice = null;
    
                    if (lastStreamStartIdx !== -1 && lastStreamEndIdx !== -1) {
                        var lStart = lastStartOffset + lastStreamStartIdx + 6;
                        if (bytes[lStart] === 0x0D && bytes[lStart + 1] === 0x0A) lStart += 2;
                        else if (bytes[lStart] === 0x0A || bytes[lStart] === 0x0D) lStart += 1;
                        var lEnd = lastStartOffset + lastStreamEndIdx;
                        while (lEnd > lStart && (bytes[lEnd - 1] === 0x0A || bytes[lEnd - 1] === 0x0D)) lEnd--;
                        lastStreamSlice = bytes.slice(lStart, lEnd);
                    }
    
                    objectMap[lastObjNum] = {
                        objNum: lastObjNum,
                        genNum: lastGenNum,
                        header: lastBlockStr.substring(0, lastStreamStartIdx !== -1 ? lastStreamStartIdx : lastBlockStr.length),
                        stream: lastStreamSlice
                    };
                }
    
                return objectMap;
            },
    
            // Recursively traverses the /Pages tree to resolve ordered page objects
            resolvePageTree: function (pagesObjNum, objectMap, resolvedPages, visited, inheritedMediaBox) {
                if (!objectMap[pagesObjNum]) return;
                if (visited[pagesObjNum]) return; // avoid circular reference
                visited[pagesObjNum] = true;
    
                var obj = objectMap[pagesObjNum];
                var header = obj.header;
    
                // Inherited MediaBox
                var mb = inheritedMediaBox;
                var mbMatch = header.match(/\/MediaBox\s*\[\s*([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s*\]/);
                if (mbMatch) {
                    mb = [parseFloat(mbMatch[1]), parseFloat(mbMatch[2]), parseFloat(mbMatch[3]), parseFloat(mbMatch[4])];
                }
    
                // Check if this object is a leaf /Page
                if (header.indexOf("/Type /Page") !== -1 || header.indexOf("/Type/Page") !== -1) {
                    resolvedPages.push({
                        pageObjNum: pagesObjNum,
                        mediaBox: mb || [0, 0, 595.28, 841.89],
                        header: header
                    });
                    return;
                }
    
                // If it is an intermediate /Pages node, parse /Kids [ ... ]
                var kidsMatch = header.match(/\/Kids\s*\[([\s\S]*?)\]/);
                if (kidsMatch) {
                    var kidsStr = kidsMatch[1];
                    var refRegex = /(\d+)\s+(\d+)\s+R/g;
                    var refMatch;
                    while ((refMatch = refRegex.exec(kidsStr)) !== null) {
                        var childObjNum = parseInt(refMatch[1], 10);
                        KeterPDFParser.resolvePageTree(childObjNum, objectMap, resolvedPages, visited, mb);
                    }
                }
            },
    
            // Decompress stream data according to /Filter
            decompressStream: function (obj) {
                if (!obj || !obj.stream || obj.stream.length === 0) return "";
                if (obj.header.indexOf("/FlateDecode") !== -1) {
                    try {
                        var uncompressedBytes = KeterInflate.decompress(obj.stream);
                        if (uncompressedBytes && uncompressedBytes.length > 0) {
                            return KeterPDFParser.bytesToString(uncompressedBytes);
                        }
                    } catch (e) {
                        KeterLogger.warn("Flate stream decompression failed on object " + obj.objNum + ": " + e.message);
                    }
                }
                return KeterPDFParser.bytesToString(obj.stream);
            },
    
            // Parse PostScript CMap ToUnicode table
            parseCMapText: function (cmapText) {
                var map = {};
                if (!cmapText) return map;
    
                // 1. beginbfchar ... endbfchar
                var bfCharMatches = cmapText.match(/\d+\s+beginbfchar([\s\S]*?)endbfchar/g);
                if (bfCharMatches) {
                    for (var i = 0; i < bfCharMatches.length; i++) {
                        var lines = bfCharMatches[i].split(/\r?\n/);
                        for (var l = 0; l < lines.length; l++) {
                            var pair = lines[l].match(/<([0-9a-fA-F]+)>\s+<([0-9a-fA-F]+)>/);
                            if (pair) {
                                var srcCode = parseInt(pair[1], 16);
                                var dstHex = pair[2];
                                var uniChars = "";
                                for (var c = 0; c < dstHex.length; c += 4) {
                                    uniChars += String.fromCharCode(parseInt(dstHex.substr(c, 4), 16));
                                }
                                map[srcCode] = uniChars;
                            }
                        }
                    }
                }
    
                // 2. beginbfrange ... endbfrange
                var bfRangeMatches = cmapText.match(/\d+\s+beginbfrange([\s\S]*?)endbfrange/g);
                if (bfRangeMatches) {
                    for (var r = 0; r < bfRangeMatches.length; r++) {
                        var rLines = bfRangeMatches[r].split(/\r?\n/);
                        for (var rl = 0; rl < rLines.length; rl++) {
                            var range1 = rLines[rl].match(/<([0-9a-fA-F]+)>\s+<([0-9a-fA-F]+)>\s+<([0-9a-fA-F]+)>/);
                            if (range1) {
                                var startC = parseInt(range1[1], 16);
                                var endC = parseInt(range1[2], 16);
                                var startDst = parseInt(range1[3], 16);
                                for (var code = startC; code <= endC; code++) {
                                    map[code] = String.fromCharCode(startDst + (code - startC));
                                }
                            }
                        }
                    }
                }
                return map;
            },
    
            // Resolve Page Fonts, ToUnicode CMaps and Widths specifically for Page N
            resolvePageFonts: function (pageHeader, objectMap) {
                var fontMap = {}; // fontTag -> { baseFont, cMap, widths, firstChar, lastChar, isToUnicodeValid }
    
                // Locate /Resources
                var resHeader = pageHeader;
                var resRefMatch = pageHeader.match(/\/Resources\s+(\d+)\s+(\d+)\s+R/);
                if (resRefMatch) {
                    var resObjNum = parseInt(resRefMatch[1], 10);
                    if (objectMap[resObjNum]) {
                        resHeader = objectMap[resObjNum].header;
                    }
                }
    
                // Find /Font << ... >>
                var fontBlockMatch = resHeader.match(/\/Font\s*<<([\s\S]*?)>>/);
                if (!fontBlockMatch) {
                    var fontRefMatch = resHeader.match(/\/Font\s+(\d+)\s+(\d+)\s+R/);
                    if (fontRefMatch && objectMap[parseInt(fontRefMatch[1], 10)]) {
                        fontBlockMatch = objectMap[parseInt(fontRefMatch[1], 10)].header.match(/<<([\s\S]*?)>>/);
                    }
                }
    
                if (fontBlockMatch) {
                    var fontDictContent = fontBlockMatch[1];
                    var fontEntryRegex = /\/(\S+)\s+(\d+)\s+(\d+)\s+R/g;
                    var fontEntry;
                    while ((fontEntry = fontEntryRegex.exec(fontDictContent)) !== null) {
                        var fontTag = fontEntry[1];
                        var fObjNum = parseInt(fontEntry[2], 10);
                        if (objectMap[fObjNum]) {
                            var fHeader = objectMap[fObjNum].header;
                            var baseFontMatch = fHeader.match(/\/BaseFont\s*\/([^\s\/>]+)/);
                            var baseFont = baseFontMatch ? baseFontMatch[1] : fontTag;
    
                            // Check /ToUnicode
                            var toUniMatch = fHeader.match(/\/ToUnicode\s+(\d+)\s+(\d+)\s+R/);
                            var cMap = {};
                            var hasToUnicode = false;
    
                            if (toUniMatch) {
                                var toUniObjNum = parseInt(toUniMatch[1], 10);
                                if (objectMap[toUniObjNum]) {
                                    var cmapStreamText = KeterPDFParser.decompressStream(objectMap[toUniObjNum]);
                                    cMap = KeterPDFParser.parseCMapText(cmapStreamText);
                                    hasToUnicode = KeterUtils.getKeyCount(cMap) > 0;
                                }
                            }
    
                            // Check /Widths
                            var widths = [];
                            var firstChar = 0, lastChar = 255;
                            var fcMatch = fHeader.match(/\/FirstChar\s+(\d+)/);
                            var lcMatch = fHeader.match(/\/LastChar\s+(\d+)/);
                            if (fcMatch) firstChar = parseInt(fcMatch[1], 10);
                            if (lcMatch) lastChar = parseInt(lcMatch[1], 10);
    
                            var wArrayMatch = fHeader.match(/\/Widths\s*\[([\s\S]*?)\]/);
                            if (!wArrayMatch) {
                                var wRefMatch = fHeader.match(/\/Widths\s+(\d+)\s+(\d+)\s+R/);
                                if (wRefMatch && objectMap[parseInt(wRefMatch[1], 10)]) {
                                    wArrayMatch = objectMap[parseInt(wRefMatch[1], 10)].header.match(/\[([\s\S]*?)\]/);
                                }
                            }
                            if (wArrayMatch) {
                                var wParts = wArrayMatch[1].split(/\s+/);
                                for (var wp = 0; wp < wParts.length; wp++) {
                                    if (wParts[wp] !== "") {
                                        widths.push(parseFloat(wParts[wp]));
                                    }
                                }
                            }
    
                            fontMap[fontTag] = {
                                baseFont: baseFont,
                                cMap: cMap,
                                hasToUnicode: hasToUnicode,
                                widths: widths,
                                firstChar: firstChar,
                                lastChar: lastChar
                            };
                        }
                    }
                }
    
                return fontMap;
            },
    
            // Decode PDF string using the specific font's CMap
            decodePDFString: function (str, fontInfo, isHex) {
                if (!str) return "";
                var cMap = (fontInfo && fontInfo.cMap) ? fontInfo.cMap : null;
                var res = "";
    
                if (isHex) {
                    for (var i = 0; i < str.length; i += 4) {
                        var hexCode = parseInt(str.substr(i, 4), 16);
                        if (cMap && cMap[hexCode]) {
                            res += cMap[hexCode];
                        } else if (hexCode >= 0x0590 && hexCode <= 0x05FF) {
                            res += String.fromCharCode(hexCode);
                        } else {
                            var b1 = (hexCode >> 8) & 0xFF;
                            var b2 = hexCode & 0xFF;
                            if (b1 > 0) res += String.fromCharCode(b1);
                            if (b2 > 0) res += String.fromCharCode(b2);
                        }
                    }
                } else {
                    var unescaped = str
                        .replace(/\\n/g, "\n")
                        .replace(/\\r/g, "\r")
                        .replace(/\\t/g, "\t")
                        .replace(/\\\(/g, "(")
                        .replace(/\\\)/g, ")")
                        .replace(/\\\\/g, "\\");
    
                    for (var j = 0; j < unescaped.length; j++) {
                        var chCode = unescaped.charCodeAt(j);
                        if (cMap && cMap[chCode]) {
                            res += cMap[chCode];
                        } else {
                            res += unescaped.charAt(j);
                        }
                    }
                }
                return res;
            },
    
            // Calculate string width using font /Widths metrics when available
            calculateSpanWidth: function (textStr, fontSize, fontInfo) {
                if (fontInfo && fontInfo.widths && fontInfo.widths.length > 0) {
                    var totalPt = 0;
                    for (var i = 0; i < textStr.length; i++) {
                        var code = textStr.charCodeAt(i);
                        var idx = code - fontInfo.firstChar;
                        if (idx >= 0 && idx < fontInfo.widths.length) {
                            totalPt += (fontInfo.widths[idx] / 1000.0) * fontSize;
                        } else {
                            totalPt += fontSize * 0.55; // measured fallback
                        }
                    }
                    return { widthPt: totalPt, isMeasured: true };
                }
                // If no /Widths table exists in font descriptor, return flag for uncertain geometry
                return { widthPt: textStr.length * (fontSize * 0.55), isMeasured: false };
            },
    
            // Extract positioned text spans from decompressed content stream of a single page
            extractPageTextSpans: function (contentStreamText, pageFonts, pageHeightPt) {
                var spans = [];
                var textBlockRegex = /BT[\s\S]*?ET/g;
                var blockMatch;
    
                while ((blockMatch = textBlockRegex.exec(contentStreamText)) !== null) {
                    var block = blockMatch[0];
                    var curFontTag = "Default";
                    var curSize = 10;
                    var curX = 0, curY = 0;
                    var leading = 12;
    
                    var ops = block.split(/\r?\n/);
                    for (var i = 0; i < ops.length; i++) {
                        var line = ops[i];
    
                        // Font selection: /F1 12 Tf
                        var tfMatch = line.match(/\/(\S+)\s+([\d\.\-]+)\s+Tf/);
                        if (tfMatch) {
                            curFontTag = tfMatch[1];
                            curSize = parseFloat(tfMatch[2]);
                            continue;
                        }
    
                        // Text matrix: [a b c d e f] Tm
                        var tmMatch = line.match(/([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+([\d\.\-]+)\s+Tm/);
                        if (tmMatch) {
                            curX = parseFloat(tmMatch[5]);
                            curY = parseFloat(tmMatch[6]);
                            continue;
                        }
    
                        // Text position: x y Td or TD
                        var tdMatch = line.match(/([\d\.\-]+)\s+([\d\.\-]+)\s+T[dD]/);
                        if (tdMatch) {
                            curX += parseFloat(tdMatch[1]);
                            curY += parseFloat(tdMatch[2]);
                            continue;
                        }
    
                        // Set leading: TL
                        var tlMatch = line.match(/([\d\.\-]+)\s+TL/);
                        if (tlMatch) {
                            leading = parseFloat(tlMatch[1]);
                            continue;
                        }
    
                        // Move to next line: T*
                        if (line.match(/T\*/)) {
                            curY -= leading;
                            continue;
                        }
    
                        // Show text: (string) Tj or <hex> Tj
                        var tjMatch = line.match(/\(([\s\S]*?)\)\s*Tj/) || line.match(/<([0-9a-fA-F]+)>\s*Tj/);
                        if (tjMatch) {
                            var rawText = tjMatch[1];
                            var fontInfo = pageFonts[curFontTag];
                            var decoded = KeterPDFParser.decodePDFString(rawText, fontInfo, line.indexOf("<") !== -1);
                            if (decoded && decoded.length > 0) {
                                var wResult = KeterPDFParser.calculateSpanWidth(decoded, curSize, fontInfo);
                                var idTop = pageHeightPt - curY - curSize;
                                spans.push({
                                    text: decoded,
                                    x: curX,
                                    y: idTop,
                                    fontSize: curSize,
                                    fontTag: curFontTag,
                                    baseFont: fontInfo ? fontInfo.baseFont : "Unknown",
                                    width: wResult.widthPt,
                                    isMeasuredWidth: wResult.isMeasured,
                                    height: curSize * 1.2
                                });
                                curX += wResult.widthPt;
                            }
                            continue;
                        }
    
                        // Show text array: [ (str) 20 (str) ] TJ
                        var bigTjMatch = line.match(/\[([\s\S]*?)\]\s*TJ/);
                        if (bigTjMatch) {
                            var arrayContent = bigTjMatch[1];
                            var fontInfo2 = pageFonts[curFontTag];
                            var elemRegex = /\(([\s\S]*?)\)|<([0-9a-fA-F]+)>|([\d\.\-]+)/g;
                            var elemMatch;
                            var chunkText = "";
                            var chunkWidth = 0;
    
                            while ((elemMatch = elemRegex.exec(arrayContent)) !== null) {
                                if (elemMatch[1] || elemMatch[2]) {
                                    var itemRaw = elemMatch[1] || elemMatch[2];
                                    var isHex = !!elemMatch[2];
                                    var dStr = KeterPDFParser.decodePDFString(itemRaw, fontInfo2, isHex);
                                    chunkText += dStr;
                                    var wSub = KeterPDFParser.calculateSpanWidth(dStr, curSize, fontInfo2);
                                    chunkWidth += wSub.widthPt;
                                } else if (elemMatch[3]) {
                                    // Kerning adjustment in 1/1000 text units
                                    var kernVal = parseFloat(elemMatch[3]);
                                    chunkWidth -= (kernVal / 1000.0) * curSize;
                                }
                            }
    
                            if (chunkText && chunkText.length > 0) {
                                var idTop2 = pageHeightPt - curY - curSize;
                                spans.push({
                                    text: chunkText,
                                    x: curX,
                                    y: idTop2,
                                    fontSize: curSize,
                                    fontTag: curFontTag,
                                    baseFont: fontInfo2 ? fontInfo2.baseFont : "Unknown",
                                    width: chunkWidth,
                                    isMeasuredWidth: !!(fontInfo2 && fontInfo2.widths && fontInfo2.widths.length > 0),
                                    height: curSize * 1.2
                                });
                                curX += chunkWidth;
                            }
                        }
                    }
                }
    
                return spans;
            }
        };
    
        // ------------------------------------------------------------------------
        // MODULE 6: SPATIAL LAYOUT & PHYSICAL GUTTER ANALYSIS (NO ARBITRARY %)
        // ------------------------------------------------------------------------
        var KeterLayoutEngine = {
            // Groups raw text spans into lines and detects physical column gutters
            analyzeLayout: function (spans, pageWidthPt, pageHeightPt) {
                if (!spans || spans.length === 0) return [];
    
                // 1. Sort spans: Top-to-Bottom, then Right-to-Left (for Hebrew)
                spans.sort(function (a, b) {
                    var yDiff = a.y - b.y;
                    if (Math.abs(yDiff) > 3.5) {
                        return yDiff;
                    }
                    return b.x - a.x; // Right-to-Left
                });
    
                // 2. Assemble into visual lines
                var lines = [];
                var curLine = null;
                for (var i = 0; i < spans.length; i++) {
                    var s = spans[i];
                    if (!curLine || Math.abs(s.y - curLine.y) > (s.fontSize * 0.45)) {
                        curLine = {
                            y: s.y,
                            fontSize: s.fontSize,
                            minX: s.x,
                            maxX: s.x + s.width,
                            spans: [s],
                            text: s.text
                        };
                        lines.push(curLine);
                    } else {
                        curLine.spans.push(s);
                        curLine.minX = Math.min(curLine.minX, s.x);
                        curLine.maxX = Math.max(curLine.maxX, s.x + s.width);
                        curLine.text += " " + s.text;
                    }
                }
    
                // 3. Detect Headers & Footers based on vertical margins
                var headerLines = [];
                var footerLines = [];
                var candidateBodyLines = [];
    
                var topMarginBoundary = pageHeightPt * 0.10;
                var bottomMarginBoundary = pageHeightPt * 0.90;
    
                for (var l = 0; l < lines.length; l++) {
                    var line = lines[l];
                    if (line.y < topMarginBoundary && line.fontSize <= 12) {
                        headerLines.push(line);
                    } else if (line.y > bottomMarginBoundary && line.fontSize <= 11) {
                        footerLines.push(line);
                    } else {
                        candidateBodyLines.push(line);
                    }
                }
    
                var regions = [];
    
                if (headerLines.length > 0) {
                    regions.push(this.buildRegion(headerLines, "RunningHeader", "Keter_RunningHeader"));
                }
    
                // 4. Physical Gutter Analysis for Body Columns (not fixed arbitrary percentages)
                var columnRegions = this.detectPhysicalColumns(candidateBodyLines, pageWidthPt);
                for (var c = 0; c < columnRegions.length; c++) {
                    regions.push(columnRegions[c]);
                }
    
                if (footerLines.length > 0) {
                    regions.push(this.buildRegion(footerLines, "PageFooter", "Keter_RunningHeader"));
                }
    
                return regions;
            },
    
            // Analyzes horizontal gaps between lines to detect true columns
            detectPhysicalColumns: function (bodyLines, pageWidthPt) {
                if (!bodyLines || bodyLines.length === 0) return [];
    
                // Calculate horizontal span frequencies
                var leftColumnLines = [];
                var rightColumnLines = [];
                var fullWidthLines = [];
    
                var midX = pageWidthPt / 2.0;
                var minGutterWidth = 14.0; // at least 14pt gutter between columns
    
                var hasLeft = false;
                var hasRight = false;
    
                for (var i = 0; i < bodyLines.length; i++) {
                    var bl = bodyLines[i];
                    var width = bl.maxX - bl.minX;
                    // If a line spans more than 70% of the page, it is a single-column or banner
                    if (width > pageWidthPt * 0.70) {
                        fullWidthLines.push(bl);
                    } else if (bl.minX > (midX - 10)) {
                        // Right column (RTL start)
                        rightColumnLines.push(bl);
                        hasRight = true;
                    } else if (bl.maxX < (midX + 10)) {
                        // Left column
                        leftColumnLines.push(bl);
                        hasLeft = true;
                    } else {
                        fullWidthLines.push(bl);
                    }
                }
    
                var resultRegions = [];
    
                // If genuine dual columns are detected
                if (hasLeft && hasRight && (leftColumnLines.length + rightColumnLines.length > fullWidthLines.length)) {
                    KeterLogger.log("Physical column gutter detected at X ≈ " + Math.round(midX) + " pt.");
                    // Right-to-Left order: Right column first, then Left column
                    if (rightColumnLines.length > 0) {
                        resultRegions.push(this.buildRegion(rightColumnLines, "Column_Right_RTL", "Keter_Main_Hebrew_RTL"));
                    }
                    if (leftColumnLines.length > 0) {
                        resultRegions.push(this.buildRegion(leftColumnLines, "Column_Left_RTL", "Keter_Main_Hebrew_RTL"));
                    }
                    if (fullWidthLines.length > 0) {
                        resultRegions.push(this.buildRegion(fullWidthLines, "Body_Spanning", "Keter_Main_Hebrew_RTL"));
                    }
                } else {
                    // Single unified text stream
                    resultRegions.push(this.buildRegion(bodyLines, "BodyText", "Keter_Main_Hebrew_RTL"));
                }
    
                return resultRegions;
            },
    
            buildRegion: function (linesList, roleName, styleName) {
                var minY = 999999, minX = 999999, maxY = 0, maxX = 0;
                var textParts = [];
    
                for (var i = 0; i < linesList.length; i++) {
                    var l = linesList[i];
                    minY = Math.min(minY, l.y);
                    minX = Math.min(minX, l.minX);
                    maxY = Math.max(maxY, l.y + (l.fontSize * 1.25));
                    maxX = Math.max(maxX, l.maxX);
                    textParts.push(l.text);
                }
    
                return {
                    role: roleName,
                    styleName: styleName,
                    bounds: [minY, minX, maxY, maxX],
                    content: textParts.join("\r")
                };
            }
        };
    
        // ------------------------------------------------------------------------
        // MODULE 7: INDESIGN DOM GENERATOR (VALIDATED API ACCESS)
        // ------------------------------------------------------------------------
        var KeterInDesignDOM = {
            setupDocument: function (templateFile, pageWidthPt, pageHeightPt) {
                var doc;
                if (templateFile && templateFile.exists) {
                    KeterLogger.log("Opening existing template: " + templateFile.fsName);
                    doc = app.open(templateFile);
                } else {
                    KeterLogger.log("Creating new InDesign document: " + Math.round(pageWidthPt) + " x " + Math.round(pageHeightPt) + " pt");
                    doc = app.documents.add();
                    doc.documentPreferences.pageWidth = pageWidthPt + "pt";
                    doc.documentPreferences.pageHeight = pageHeightPt + "pt";
                    doc.documentPreferences.facingPages = true;
                }
    
                // CRITICAL FIX: Explicitly lock document measurement units to Points
                // to prevent coordinate scaling errors when user InDesign uses mm or picas
                try {
                    doc.viewPreferences.horizontalMeasurementUnits = MeasurementUnits.POINTS;
                    doc.viewPreferences.verticalMeasurementUnits = MeasurementUnits.POINTS;
                    doc.viewPreferences.rulerOrigin = RulerOrigin.PAGE_ORIGIN;
                } catch (eUnits) {
                    KeterLogger.warn("Measurement unit assignment warning: " + eUnits.message);
                }
    
                this.ensureLayers(doc);
                this.ensureStyles(doc);
    
                return doc;
            },
    
            ensureLayers: function (doc) {
                // PDF Reference Underlay (locked, bottom)
                var underlayLayer = doc.layers.itemByName(KeterConfig.layerUnderlay);
                if (!underlayLayer.isValid) {
                    // UIColors safety check: never reference bare UIColors
                    underlayLayer = doc.layers.add({ name: KeterConfig.layerUnderlay });
                }
                underlayLayer.move(LocationOptions.AT_END);
    
                // Reconstructed Editable Text (active, top)
                var textLayer = doc.layers.itemByName(KeterConfig.layerText);
                if (!textLayer.isValid) {
                    textLayer = doc.layers.add({ name: KeterConfig.layerText });
                }
                doc.activeLayer = textLayer;
            },
    
            ensureStyles: function (doc) {
                var bestHebrewFont = this.findBestAvailableFont(KeterConfig.hebrewFontFallbacks);
                KeterLogger.log("Selected primary Hebrew font: " + bestHebrewFont);
    
                this.createOrUpdateParaStyle(doc, "Keter_Main_Hebrew_RTL", {
                    appliedFont: bestHebrewFont,
                    pointSize: 12,
                    leading: 15.5,
                    justification: Justification.FULLY_JUSTIFIED,
                    isRTL: true
                });
    
                this.createOrUpdateParaStyle(doc, "Keter_RunningHeader", {
                    appliedFont: bestHebrewFont,
                    pointSize: 9,
                    leading: 11,
                    justification: Justification.CENTER_ALIGN,
                    isRTL: true
                });
            },
    
            findBestAvailableFont: function (candidates) {
                for (var i = 0; i < candidates.length; i++) {
                    var candidate = candidates[i];
                    try {
                        var f = app.fonts.itemByName(candidate);
                        if (f && f.isValid) {
                            return candidate;
                        }
                    } catch (e) {}
                }
                KeterLogger.stats.fontSubstitutions.push("Times New Roman (Hebrew fallback)");
                return "Times New Roman";
            },
    
            createOrUpdateParaStyle: function (doc, styleName, props) {
                var style = doc.paragraphStyles.itemByName(styleName);
                if (!style.isValid) {
                    style = doc.paragraphStyles.add({ name: styleName });
                }
    
                try {
                    if (props.appliedFont) style.appliedFont = props.appliedFont;
                    if (props.pointSize) style.pointSize = props.pointSize;
                    if (props.leading) style.leading = props.leading;
                    if (props.justification) style.justification = props.justification;
    
                    // Enforce Adobe World-Ready Paragraph Composer
                    try {
                        style.composer = "Adobe World-Ready Paragraph Composer";
                    } catch (e1) {
                        try {
                            style.composer = "$ID/Adobe World-Ready Paragraph Composer";
                        } catch (e2) {}
                    }
    
                    // Enforce Right-to-Left Direction
                    if (props.isRTL) {
                        try {
                            style.paragraphDirection = ParagraphDirectionOptions.RIGHT_TO_LEFT_DIRECTION;
                        } catch (e3) {
                            try {
                                style.paragraphDirection = 1278366308;
                            } catch (e4) {}
                        }
                    }
                } catch (ex) {
                    KeterLogger.warn("Style configuration note for '" + styleName + "': " + ex.message);
                }
                return style;
            },
    
            placePDFUnderlay: function (doc, page, pdfFile, pageNum, opacity) {
                try {
                    var underlayLayer = doc.layers.itemByName(KeterConfig.layerUnderlay);
                    underlayLayer.locked = false;
                    doc.activeLayer = underlayLayer;
    
                    try {
                        app.pdfPlacePreferences.pdfCrop = PDFCrop.CROP_MEDIA;
                    } catch (eCrop) {
                        try { app.pdfPlacePreferences.pdfCrop = PDFCrop.cropMedia; } catch (eCrop2) {}
                    }
                    app.pdfPlacePreferences.pageNumber = pageNum;
                    app.pdfPlacePreferences.transparentBackground = true;
    
                    var placed = page.place(pdfFile, [0, 0])[0];
                    if (placed && placed.isValid && opacity && opacity < 100) {
                        try {
                            placed.transparencySettings.blendingSettings.opacity = opacity;
                        } catch (eOp) {}
                    }
    
                    underlayLayer.locked = true;
                    doc.activeLayer = doc.layers.itemByName(KeterConfig.layerText);
                    KeterLogger.log("Placed 1:1 PDF underlay reference for page " + pageNum);
                    return true;
                } catch (ex) {
                    KeterLogger.error("Failed to place PDF underlay for page " + pageNum + ": " + ex.message);
                    return false;
                }
            },
    
            createTextFrame: function (doc, page, bounds, content, styleName) {
                try {
                    var textLayer = doc.layers.itemByName(KeterConfig.layerText);
                    doc.activeLayer = textLayer;
    
                    var top = Math.max(0, bounds[0]);
                    var left = Math.max(0, bounds[1]);
                    var bottom = Math.max(top + 16, bounds[2]);
                    var right = Math.max(left + 24, bounds[3]);
    
                    var frame = page.textFrames.add(textLayer, LocationOptions.UNKNOWN_VALUE, {
                        geometricBounds: [top, left, bottom, right]
                    });
    
                    // Apply World-Ready Composer directly to frame parent story
                    try {
                        frame.parentStory.composer = "Adobe World-Ready Paragraph Composer";
                        frame.parentStory.paragraphDirection = ParagraphDirectionOptions.RIGHT_TO_LEFT_DIRECTION;
                    } catch (eWRC) {}
    
                    // Assign Unicode text contents
                    frame.contents = content;
    
                    var pStyle = doc.paragraphStyles.itemByName(styleName);
                    if (pStyle.isValid) {
                        frame.parentStory.texts.item(0).applyParagraphStyle(pStyle, true);
                    }
    
                    KeterLogger.stats.textFramesCreated++;
                    KeterLogger.stats.charactersExtracted += content.length;
    
                    if (frame.overflows) {
                        KeterLogger.stats.oversetFrames++;
                        KeterLogger.warn("Overset text in frame on page " + page.name + " (" + styleName + "). Frame resized slightly or leading adjustment required.");
                    }
    
                    return frame;
                } catch (ex) {
                    KeterLogger.error("Error creating InDesign text frame: " + ex.message);
                    return null;
                }
            }
        };
    
        // ------------------------------------------------------------------------
        // MODULE 8: USER INTERFACE (SCRIPTUI DIALOG & PROGRESS WINDOW)
        // ------------------------------------------------------------------------
        var KeterUI = {
            showMainDialog: function () {
                var w = new Window("dialog", KeterConfig.name + " v" + KeterConfig.version);
                w.orientation = "column";
                w.alignChildren = ["fill", "top"];
                w.spacing = 10;
                w.margins = 16;
    
                var headerPnl = w.add("panel", undefined, undefined, { borderStyle: "black" });
                headerPnl.orientation = "column";
                headerPnl.alignChildren = ["fill", "top"];
                headerPnl.margins = 10;
    
                var titleTxt = headerPnl.add("statictext", undefined, "KETER PDF-TO-INDESIGN CONVERSION ENGINE");
                titleTxt.graphics.font = ScriptUI.newFont(w.graphics.font.name, ScriptUI.FontStyle.BOLD, 14);
                headerPnl.add("statictext", undefined, "Audited Production Engine: Strict Truth-in-Extraction Architecture");
    
                // SOURCE PDF
                var srcPnl = w.add("panel", undefined, "Source PDF & Options");
                srcPnl.orientation = "column";
                srcPnl.alignChildren = ["fill", "top"];
                srcPnl.margins = 12;
                srcPnl.spacing = 8;
    
                var pdfRow = srcPnl.add("group");
                pdfRow.orientation = "row";
                var pdfPathInput = pdfRow.add("edittext", undefined, "", { readonly: true });
                pdfPathInput.characters = 42;
                var browsePdfBtn = pdfRow.add("button", undefined, "Browse PDF...");
    
                var pdfInfoTxt = srcPnl.add("statictext", undefined, "Select a PDF to inspect structure and content streams.");
    
                var optGroup = srcPnl.add("group");
                optGroup.orientation = "column";
                optGroup.alignChildren = ["left", "top"];
                optGroup.spacing = 4;
    
                var underlayCb = optGroup.add("checkbox", undefined, "Place original PDF page as locked 1:1 visual reference underlay");
                underlayCb.value = true;
    
                var opacityRow = optGroup.add("group");
                opacityRow.orientation = "row";
                opacityRow.add("statictext", undefined, "Underlay Opacity:");
                var opacityDropdown = opacityRow.add("dropdownlist", undefined, ["25%", "40% (Recommended)", "60%", "100% (Solid)"]);
                opacityDropdown.selection = 1;
    
                var pageRangeRow = srcPnl.add("group");
                pageRangeRow.orientation = "row";
                pageRangeRow.add("statictext", undefined, "Page Range to Process:");
                var pageRangeInput = pageRangeRow.add("edittext", undefined, "All");
                pageRangeInput.characters = 12;
                pageRangeRow.add("statictext", undefined, "(e.g., 'All', '1-5')");
    
                var btnRow = w.add("group");
                btnRow.orientation = "row";
                btnRow.alignment = "right";
                var cancelBtn = btnRow.add("button", undefined, "Cancel", { name: "cancel" });
                var convertBtn = btnRow.add("button", undefined, "Start Conversion", { name: "ok" });
                convertBtn.enabled = false;
    
                var selectedFile = null;
    
                browsePdfBtn.onClick = function () {
                    var f = File.openDialog("Select Source PDF File", "PDF Files:*.pdf;All Files:*.*");
                    if (f) {
                        selectedFile = f;
                        pdfPathInput.text = f.fsName;
                        pdfInfoTxt.text = "Inspecting PDF: " + f.name + "...";
                        convertBtn.enabled = true;
                    }
                };
    
                if (w.show() === 1) {
                    var opacityVal = 40;
                    if (opacityDropdown.selection.index === 0) opacityVal = 25;
                    if (opacityDropdown.selection.index === 1) opacityVal = 40;
                    if (opacityDropdown.selection.index === 2) opacityVal = 60;
                    if (opacityDropdown.selection.index === 3) opacityVal = 100;
    
                    return {
                        pdfFile: selectedFile,
                        placeUnderlay: underlayCb.value,
                        underlayOpacity: opacityVal,
                        pageRange: pageRangeInput.text
                    };
                }
                return null;
            },
    
            showProgress: function (title, maxVal) {
                var pw = new Window("palette", title);
                pw.orientation = "column";
                pw.alignChildren = ["fill", "top"];
                pw.margins = 16;
                pw.spacing = 10;
    
                var statusTxt = pw.add("statictext", undefined, "Initializing conversion pipeline...");
                statusTxt.characters = 45;
    
                var pBar = pw.add("progressbar", undefined, 0, maxVal);
                pBar.preferredSize.width = 320;
    
                pw.center();
                pw.show();
    
                return {
                    update: function (val, text) {
                        pBar.value = val;
                        if (text) statusTxt.text = text;
                        pw.update();
                    },
                    close: function () {
                        pw.close();
                    }
                };
            }
        };
    
        // ------------------------------------------------------------------------
        // MODULE 9: CONVERSION PIPELINE (STRICT & HONEST EXTRACTION)
        // ------------------------------------------------------------------------
        function parsePageRange(rangeStr, totalPages) {
            var pages = [];
            if (!rangeStr || rangeStr.toLowerCase() === "all") {
                for (var i = 1; i <= totalPages; i++) pages.push(i);
                return pages;
            }
            var parts = rangeStr.split(",");
            for (var p = 0; p < parts.length; p++) {
                var part = KeterUtils.trim(parts[p]);
                if (part.indexOf("-") !== -1) {
                    var range = part.split("-");
                    var start = parseInt(range[0], 10);
                    var end = parseInt(range[1], 10);
                    if (!isNaN(start) && !isNaN(end)) {
                        for (var r = start; r <= Math.min(end, totalPages); r++) {
                            pages.push(r);
                        }
                    }
                } else {
                    var single = parseInt(part, 10);
                    if (!isNaN(single) && single >= 1 && single <= totalPages) {
                        pages.push(single);
                    }
                }
            }
            return pages.length > 0 ? pages : [1];
        }
    
        function runConversion(options) {
            KeterLogger.log("Starting Keter conversion: " + options.pdfFile.fsName);
    
            // 1. Read binary bytes and raw string
            var bytes = KeterPDFParser.readFileBytes(options.pdfFile);
            var rawStr = KeterPDFParser.bytesToString(bytes);
    
            // 2. Index PDF objects
            var objectMap = KeterPDFParser.indexPDFObjects(rawStr, bytes);
            var totalObjects = KeterUtils.getKeyCount(objectMap);
            KeterLogger.log("Indexed " + totalObjects + " indirect PDF objects.");
    
            // 3. Locate Root Catalog & Pages Tree
            var catalogObjNum = null;
            var pagesObjNum = null;
    
            var rootMatch = rawStr.match(/\/Root\s+(\d+)\s+(\d+)\s+R/);
            if (rootMatch) {
                catalogObjNum = parseInt(rootMatch[1], 10);
                if (objectMap[catalogObjNum]) {
                    var catHeader = objectMap[catalogObjNum].header;
                    var pagesMatch = catHeader.match(/\/Pages\s+(\d+)\s+(\d+)\s+R/);
                    if (pagesMatch) {
                        pagesObjNum = parseInt(pagesMatch[1], 10);
                    }
                }
            }
    
            // Fallback: search for /Type /Pages object
            if (!pagesObjNum) {
                for (var objId in objectMap) {
                    if (objectMap[objId].header.indexOf("/Type /Pages") !== -1 || objectMap[objId].header.indexOf("/Type/Pages") !== -1) {
                        pagesObjNum = parseInt(objId, 10);
                        break;
                    }
                }
            }
    
            // 4. Resolve Page Objects through Pages Tree
            var resolvedPages = [];
            if (pagesObjNum) {
                KeterPDFParser.resolvePageTree(pagesObjNum, objectMap, resolvedPages, {}, null);
            }
    
            var totalPages = resolvedPages.length;
            if (totalPages === 0) {
                // Scan /Type /Page directly if Pages tree was obfuscated
                for (var pId in objectMap) {
                    if (objectMap[pId].header.indexOf("/Type /Page") !== -1 || objectMap[pId].header.indexOf("/Type/Page") !== -1) {
                        resolvedPages.push({
                            pageObjNum: parseInt(pId, 10),
                            mediaBox: [0, 0, 595.28, 841.89],
                            header: objectMap[pId].header
                        });
                    }
                }
                totalPages = resolvedPages.length;
            }
    
            if (totalPages === 0) {
                totalPages = 1;
            }
    
            KeterLogger.reset(totalPages);
            KeterLogger.log("Resolved " + totalPages + " pages from PDF object graph.");
    
            var targetPages = parsePageRange(options.pageRange, totalPages);
            var progress = KeterUI.showProgress("Reconstructing InDesign Document", targetPages.length + 2);
            progress.update(0, "Setting up InDesign document & World-Ready styles...");
    
            // 5. Establish InDesign Document matching initial page geometry
            var initialMediaBox = (resolvedPages.length > 0 && resolvedPages[0].mediaBox) ? resolvedPages[0].mediaBox : [0, 0, 595.28, 841.89];
            var pageWidthPt = Math.abs(initialMediaBox[2] - initialMediaBox[0]);
            var pageHeightPt = Math.abs(initialMediaBox[3] - initialMediaBox[1]);
    
            var doc = KeterInDesignDOM.setupDocument(null, pageWidthPt, pageHeightPt);
    
            while (doc.pages.length < targetPages.length) {
                doc.pages.add(LocationOptions.AT_END);
            }
    
            // 6. Process each selected page
            for (var pIdx = 0; pIdx < targetPages.length; pIdx++) {
                var pageNum = targetPages[pIdx];
                var idPage = doc.pages[pIdx];
                progress.update(pIdx + 1, "Processing Page " + pageNum + " of " + targetPages.length + "...");
    
                // Always place PDF 1:1 visual reference underlay when requested
                if (options.placeUnderlay) {
                    KeterInDesignDOM.placePDFUnderlay(doc, idPage, options.pdfFile, pageNum, options.underlayOpacity);
                }
    
                var pageMeta = (pageNum - 1 < resolvedPages.length) ? resolvedPages[pageNum - 1] : null;
                if (!pageMeta) {
                    KeterLogger.recordPageAudit(
                        pageNum,
                        "REFERENCE_UNDERLAY_ONLY",
                        "Page structure could not be resolved from object tree. Visual underlay placed.",
                        0,
                        0
                    );
                    continue;
                }
    
                // Extract page /Contents object(s)
                var contentsMatches = [];
                var cRefRegex = /\/Contents\s+(\d+)\s+(\d+)\s+R/g;
                var cMatch;
                while ((cMatch = cRefRegex.exec(pageMeta.header)) !== null) {
                    contentsMatches.push(parseInt(cMatch[1], 10));
                }
    
                // Also check for /Contents [ 12 0 R 13 0 R ]
                var cArrayMatch = pageMeta.header.match(/\/Contents\s*\[([\s\S]*?)\]/);
                if (cArrayMatch) {
                    var cArrRefs = cArrayMatch[1].match(/(\d+)\s+(\d+)\s+R/g);
                    if (cArrRefs) {
                        for (var cr = 0; cr < cArrRefs.length; cr++) {
                            var idNum = parseInt(cArrRefs[cr].split(/\s+/)[0], 10);
                            if (!KeterUtils.arrayContains(contentsMatches, idNum)) {
                                contentsMatches.push(idNum);
                            }
                        }
                    }
                }
    
                // If no contents objects could be located
                if (contentsMatches.length === 0) {
                    KeterLogger.recordPageAudit(
                        pageNum,
                        "REFERENCE_UNDERLAY_ONLY",
                        "No /Contents stream found (Page may be scanned raster image or use ObjStm). Visual underlay preserved.",
                        0,
                        0
                    );
                    continue;
                }
    
                // Concatenate all content streams for this page
                var pageContentText = "";
                for (var cIdx = 0; cIdx < contentsMatches.length; cIdx++) {
                    var cObjNum = contentsMatches[cIdx];
                    if (objectMap[cObjNum]) {
                        pageContentText += KeterPDFParser.decompressStream(objectMap[cObjNum]) + "\n";
                    }
                }
    
                // Check if content stream has any text operators (BT...ET)
                if (pageContentText.indexOf("BT") === -1 || pageContentText.indexOf("ET") === -1) {
                    KeterLogger.recordPageAudit(
                        pageNum,
                        "REFERENCE_UNDERLAY_ONLY",
                        "Content stream contains vector/raster artwork only (no text operators BT...ET). Visual underlay preserved.",
                        0,
                        0
                    );
                    continue;
                }
    
                // Resolve page-specific fonts and ToUnicode CMaps
                var pageFonts = KeterPDFParser.resolvePageFonts(pageMeta.header, objectMap);
    
                // Extract text spans
                var curPageHeightPt = Math.abs(pageMeta.mediaBox[3] - pageMeta.mediaBox[1]);
                var curPageWidthPt = Math.abs(pageMeta.mediaBox[2] - pageMeta.mediaBox[0]);
                var spans = KeterPDFParser.extractPageTextSpans(pageContentText, pageFonts, curPageHeightPt);
    
                if (spans.length === 0) {
                    KeterLogger.recordPageAudit(
                        pageNum,
                        "REFERENCE_UNDERLAY_ONLY",
                        "Text operators could not be mapped to Unicode characters (Font encodings / ToUnicode missing). Visual underlay preserved.",
                        0,
                        0
                    );
                    continue;
                }
    
                // Gutter and layout analysis
                var regions = KeterLayoutEngine.analyzeLayout(spans, curPageWidthPt, curPageHeightPt);
    
                // Create editable InDesign Text Frames
                var pageCharCount = 0;
                for (var r = 0; r < regions.length; r++) {
                    var reg = regions[r];
                    KeterInDesignDOM.createTextFrame(doc, idPage, reg.bounds, reg.content, reg.styleName);
                    pageCharCount += reg.content.length;
                }
    
                KeterLogger.recordPageAudit(
                    pageNum,
                    "EDITABLE_RECONSTRUCTED",
                    "Successfully reconstructed " + regions.length + " editable text frame(s).",
                    regions.length,
                    pageCharCount
                );
            }
    
            progress.update(targetPages.length + 1, "Saving diagnostic report...");
            progress.close();
    
            // 7. Save Audit Report
            var folder = options.pdfFile.parent;
            var baseName = options.pdfFile.name.replace(/\.pdf$/i, "");
            var reportFile = KeterLogger.saveReportFile(folder.fsName, baseName, doc.name, options.pdfFile.fsName);
    
            // 8. Display Completion Summary Dialog
            var summaryMsg = "Keter Conversion Completed!\n\n" +
                "• Total Pages Processed: " + targetPages.length + "\n" +
                "• Pages Converted to Editable Text: " + KeterLogger.stats.editablePagesCount + "\n" +
                "• Pages Preserved as 1:1 Visual Underlay: " + KeterLogger.stats.underlayOnlyPagesCount + "\n" +
                "• Text Frames Created: " + KeterLogger.stats.textFramesCreated + "\n" +
                "• Recovered Characters: " + KeterLogger.stats.charactersExtracted + "\n" +
                "• Overset Text Warnings: " + KeterLogger.stats.oversetFrames + "\n\n" +
                "Diagnostic report saved to:\n" + (reportFile ? reportFile.fsName : "Report generated in log.");
    
            alert(summaryMsg, KeterConfig.name);
        }
    
        // ------------------------------------------------------------------------
        // ENTRY POINT: SINGLE UNDOABLE TRANSACTION
        // ------------------------------------------------------------------------
        function main() {
            if (typeof app === "undefined" || app.documents === undefined) {
                alert("This script must be run directly inside Adobe InDesign.", "Keter Engine Error");
                return;
            }
    
            var options = KeterUI.showMainDialog();
            if (!options) return;
    
            app.doScript(
                function () {
                    try {
                        runConversion(options);
                    } catch (err) {
                        alert("Conversion encountered a critical error:\n" + err.message + "\nLine: " + (err.line || "unknown"), "Keter Error");
                        KeterLogger.error("CRITICAL RUNTIME ERROR: " + err.message + " (Line " + (err.line || "unknown") + ")");
                    }
                },
                ScriptLanguage.JAVASCRIPT,
                [],
                UndoModes.ENTIRE_SCRIPT,
                "Keter PDF to InDesign Conversion"
            );
        }
    
        main();
    
    })();
    
    
    עזרה הדדית - עימוד

  • בקשת מידע | PDF באינדיזיין
    א אברהם יצחק יעקב

    @דור-נאמן יש סקריפט שעושה את זה? כי אינדיזיין יש לי על מחשב אופליין, בקיצער, סיבוכים...
    אחד מהמתכנתים פה יכולים לעשות סקריפט לזה? אם מישהו יכול לתייג

    עזרה הדדית - עימוד

  • בקשת מידע | PDF באינדיזיין
    א אברהם יצחק יעקב

    @משהו-חדש לי אין את זה, ובדקתי גם אצל אחרים. גם להם אין.
    יש משהו כסקריפט?

    עזרה הדדית - עימוד

  • בקשת מידע | PDF באינדיזיין
    א אברהם יצחק יעקב

    יש תוסף או סקריפט שפותח מסמך PDF כקובץ אינדיזיין מוכן לעריכה?
    כעת יש אפשרות להכניס PDF כמסמך, אני רוצה לדעת אם יש דרך לפתוח את זה.
    אם אין, אז יש דרך לעשות את זה לבד? מישהו מוכן לעשות את זה?

    עזרה הדדית - עימוד

  • סקר | השוואה ביצירת תלת מימד בין מודלי הדגל השונים
    א אברהם יצחק יעקב

    @חמור-חמורותיים אתה יכול להעלות את התיקיה בלי IMG וזיפ? זה פשוט לא נפתח לי במחשב, כי זה IMG

    עזרה הדדית - בינה מלאכותית

  • שיתוף | מה חדש בבינה מלאכותית - נספח עדכוני פיצ'רים ודיונים 💬
    א אברהם יצחק יעקב

    @חמור-חמורותיים משפט שדה....

    עזרה הדדית - בינה מלאכותית

  • להורדה | jadx כלי להנדסה לאחור של אפליקציה, והפעם ממשק בעברית, ועם עוזר ai בתוך התוכנה!
    א אברהם יצחק יעקב

    @שמואל-הסיני יש עדכונים?

    תוצרת הבנייה

  • בירור | חסימה מוחלטת למחשב
    א אברהם יצחק יעקב

    @YAHBDK

    כל דבר אפשר לעקוף

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | חסימה מוחלטת למחשב
    א אברהם יצחק יעקב

    https://www.techloq.com/products/plans-pricing.
    זה עובד חלק. לא משנה איפה אתה גר, זה פשוט חוסם את המחשב מאפשרות של אינטרנט

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | חסימה מוחלטת למחשב
    א אברהם יצחק יעקב

    זה בסדר כבר הסתדרתי.
    התקנתי תוכנה של טקלוק מארה"ב חינמית חסימה מוחלטת למחשב

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | חסימה מוחלטת למחשב
    א אברהם יצחק יעקב

    תגובה: סיכום אפשרויות סינונים על המחשב עצמו

    יש תוכנה שחוסמת לגמרי את המחשב מאינטרנט? היה לי פעם ואני לא מצליח למצוא את זה

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | האםהתוכנות תחליף לאדובי החדש שיצרו הוא בעברית מלאה? או ניתן לתרגום?
    א אברהם יצחק יעקב

    @מחנה-ידידים אינדיזיין? יש!!!!

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | האםהתוכנות תחליף לאדובי החדש שיצרו הוא בעברית מלאה? או ניתן לתרגום?
    א אברהם יצחק יעקב

    @מחנה-ידידים כתב:

    כמה דק'?

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | האםהתוכנות תחליף לאדובי החדש שיצרו הוא בעברית מלאה? או ניתן לתרגום?
    א אברהם יצחק יעקב

    @מחנה-ידידים ובמילים פשוטות?

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | האםהתוכנות תחליף לאדובי החדש שיצרו הוא בעברית מלאה? או ניתן לתרגום?
    א אברהם יצחק יעקב

    @מחנה-ידידים מה למשל? יש לינק לאיפשהו?

    עזרה הדדית - מחשבים וטכנולוגיה

  • בירור | האםהתוכנות תחליף לאדובי החדש שיצרו הוא בעברית מלאה? או ניתן לתרגום?
    א אברהם יצחק יעקב

    @מחנה-ידידים איזה באגים? משהו רציני?

    עזרה הדדית - מחשבים וטכנולוגיה
  • התחברות

  • אין לך חשבון עדיין? הרשמה

  • התחברו או הירשמו כדי לחפש.
  • פוסט ראשון
    פוסט אחרון
0
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר