0

I have this basically:

const text = `וַיֹּאמֶר אֲבִימֶלֶךְ וּפִיכֹל שַׂר־צְבָאוֹ אֶל־אַבְרָהָם לֵאמֹר אֱלֹהִים עִמְּךָ בְּכֹל אֲשֶׁר־אַתָּה עֹשֶׂה׃  וְעַתָּה הִשָּׁבְעָה לִּי בֵאלֹהִים הֵנָּה אִם־תִּשְׁקֹר לִי וּלְנִינִי וּלְנֶכְדִּי כַּחֶסֶד אֲשֶׁר־עָשִׂיתִי עִמְּךָ תַּעֲשֶׂה עִמָּדִי וְעִם־הָאָרֶץ אֲשֶׁר־גַּרְתָּה בָּהּ׃  וַיֹּאמֶר אַבְרָהָם אָנֹכִי אִשָּׁבֵעַ׃  וְהוֹכִחַ אַבְרָהָם אֶת־אֲבִימֶלֶךְ עַל־אֹדוֹת בְּאֵר הַמַּיִם אֲשֶׁר גָּזְלוּ עַבְדֵי אֲבִימֶלֶךְ׃  וַיֹּאמֶר אֲבִימֶלֶךְ לֹא יָדַעְתִּי מִי עָשָׂה אֶת־הַדָּבָר הַזֶּה וְגַם־אַתָּה לֹא־הִגַּדְתָּ לִּי וְגַם אָנֹכִי לֹא שָׁמַעְתִּי בִּלְתִּי הַיּוֹם׃  וַיִּקַּח אַבְרָהָם צֹאן וּבָקָר וַיִּתֵּן לַאֲבִימֶלֶךְ וַיִּכְרְתוּ שְׁנֵיהֶם בְּרִית׃  וַיַּצֵּב אַבְרָהָם אֶת־שֶׁבַע כִּבְשֹׂת הַצֹּאן לְבַדְּהֶן׃  וַיֹּאמֶר אֲבִימֶלֶךְ אֶל־אַבְרָהָם מָה הֵנָּה שֶׁבַע כְּבָשֹׂת הָאֵלֶּה אֲשֶׁר הִצַּבְתָּ לְבַדָּנָה׃  וַיֹּאמֶר כִּי אֶת־שֶׁבַע כְּבָשֹׂת תִּקַּח מִיָּדִי בַּעֲבוּר תִּהְיֶה־לִּי לְעֵדָה כִּי חָפַרְתִּי אֶת־הַבְּאֵר הַזֹּאת׃  עַל־כֵּן קָרָא לַמָּקוֹם הַהוּא בְּאֵר שָׁבַע כִּי שָׁם נִשְׁבְּעוּ שְׁנֵיהֶם׃  וַיִּכְרְתוּ בְרִית בִּבְאֵר שָׁבַע וַיָּקָם אֲבִימֶלֶךְ וּפִיכֹל שַׂר־צְבָאוֹ וַיָּשֻׁבוּ אֶל־אֶרֶץ פְּלִשְׁתִּים׃  וַיִּטַּע אֶשֶׁל בִּבְאֵר שָׁבַע וַיִּקְרָא־שָׁם בְּשֵׁם יְהוָה אֵל עוֹלָם׃  וַיָּגָר אַבְרָהָם בְּאֶרֶץ פְּלִשְׁתִּים יָמִים רַבִּים׃     וַיְהִי אַחַר הַדְּבָרִים הָאֵלֶּה וְהָאֱלֹהִים נִסָּה אֶת־אַבְרָהָם וַיֹּאמֶר אֵלָיו אַבְרָהָם וַיֹּאמֶר הִנֵּנִי׃  וַיֹּאמֶר קַח־נָא אֶת־בִּנְךָ אֶת־יְחִידְךָ אֲשֶׁר־אָהַבְתָּ אֶת־יִצְחָק וְלֶךְ־לְךָ אֶל־אֶרֶץ הַמֹּרִיָּה וְהַעֲלֵהוּ שָׁם לְעֹלָה עַל אַחַד הֶהָרִים אֲשֶׁר אֹמַר אֵלֶיךָ׃  וַיַּשְׁכֵּם אַבְרָהָם בַּבֹּקֶר וַיַּחֲבֹשׁ אֶת־חֲמֹרוֹ וַיִּקַּח אֶת־שְׁנֵי נְעָרָיו אִתּוֹ וְאֵת יִצְחָק בְּנוֹ וַיְבַקַּע עֲצֵי עֹלָה וַיָּקָם וַיֵּלֶךְ אֶל־הַמָּקוֹם אֲשֶׁר־אָמַר־לוֹ הָאֱלֹהִים׃  בַּיּוֹם הַשְּׁלִישִׁי וַיִּשָּׂא אַבְרָהָם אֶת־עֵינָיו וַיַּרְא אֶת־הַמָּקוֹם מֵרָחֹק׃  וַיֹּאמֶר אַבְרָהָם אֶל־נְעָרָיו שְׁבוּ־לָכֶם פֹּה עִם־הַחֲמוֹר וַאֲנִי וְהַנַּעַר נֵלְכָה עַד־כֹּה וְנִשְׁתַּחֲוֶה וְנָשׁוּבָה אֲלֵיכֶם׃  וַיִּקַּח אַבְרָהָם אֶת־עֲצֵי הָעֹלָה וַיָּשֶׂם עַל־יִצְחָק בְּנוֹ וַיִּקַּח בְּיָדוֹ אֶת־הָאֵשׁ וְאֶת־הַמַּאֲכֶלֶת וַיֵּלְכוּ שְׁנֵיהֶם יַחְדָּו׃  וַיֹּאמֶר יִצְחָק אֶל־אַבְרָהָם אָבִיו וַיֹּאמֶר אָבִי וַיֹּאמֶר הִנֶּנִּי בְנִי וַיֹּאמֶר הִנֵּה הָאֵשׁ וְהָעֵצִים וְאַיֵּה הַשֶּׂה לְעֹלָה׃  וַיֹּאמֶר אַבְרָהָם אֱלֹהִים יִרְאֶה־לּוֹ הַשֶּׂה לְעֹלָה בְּנִי וַיֵּלְכוּ שְׁנֵיהֶם יַחְדָּו׃  וַיָּבֹאוּ אֶל־הַמָּקוֹם אֲשֶׁר אָמַר־לוֹ הָאֱלֹהִים וַיִּבֶן שָׁם אַבְרָהָם אֶת־הַמִּזְבֵּחַ וַיַּעֲרֹךְ אֶת־הָעֵצִים וַיַּעֲקֹד אֶת־יִצְחָק בְּנוֹ וַיָּשֶׂם אֹתוֹ עַל־הַמִּזְבֵּחַ מִמַּעַל לָעֵצִים׃  וַיִּשְׁלַח אַבְרָהָם אֶת־יָדוֹ וַיִּקַּח אֶת־הַמַּאֲכֶלֶת לִשְׁחֹט אֶת־בְּנוֹ׃  וַיִּקְרָא אֵלָיו מַלְאַךְ יְהוָה מִן־הַשָּׁמַיִם וַיֹּאמֶר אַבְרָהָם אַבְרָהָם וַיֹּאמֶר הִנֵּנִי׃  וַיֹּאמֶר אַל־תִּשְׁלַח יָדְךָ אֶל־הַנַּעַר וְאַל־תַּעַשׂ לוֹ מְאוּמָּה כִּי עַתָּה יָדַעְתִּי כִּי־יְרֵא אֱלֹהִים אַתָּה וְלֹא חָשַׂכְתָּ אֶת־בִּנְךָ אֶת־יְחִידְךָ מִמֶּנִּי׃  וַיִּשָּׂא אַבְרָהָם אֶת־עֵינָיו וַיַּרְא וְהִנֵּה־אַיִל אַחַר נֶאֱחַז בַּסְּבַךְ בְּקַרְנָיו וַיֵּלֶךְ אַבְרָהָם וַיִּקַּח אֶת־הָאַיִל וַיַּעֲלֵהוּ לְעֹלָה תַּחַת בְּנוֹ׃  וַיִּקְרָא אַבְרָהָם שֵׁם־הַמָּקוֹם הַהוּא יְהוָה יִרְאֶה אֲשֶׁר יֵאָמֵר הַיּוֹם בְּהַר יְהוָה יֵרָאֶה׃  וַיִּקְרָא מַלְאַךְ יְהוָה אֶל־אַבְרָהָם שֵׁנִית מִן־הַשָּׁמָיִם׃  וַיֹּאמֶר בִּי נִשְׁבַּעְתִּי נְאֻם־יְהוָה כִּי יַעַן אֲשֶׁר עָשִׂיתָ אֶת־הַדָּבָר הַזֶּה וְלֹא חָשַׂכְתָּ אֶת־בִּנְךָ אֶת־יְחִידֶךָ׃  כִּי־בָרֵךְ אֲבָרֶכְךָ וְהַרְבָּה אַרְבֶּה אֶת־זַרְעֲךָ כְּכוֹכְבֵי הַשָּׁמַיִם וְכַחוֹל אֲשֶׁר עַל־שְׂפַת הַיָּם וְיִרַשׁ זַרְעֲךָ אֵת שַׁעַר אֹיְבָיו׃  וְהִתְבָּרֲכוּ בְזַרְעֲךָ כֹּל גּוֹיֵי הָאָרֶץ עֵקֶב אֲשֶׁר שָׁמַעְתָּ בְּקֹלִי׃  וַיָּשָׁב אַבְרָהָם אֶל־נְעָרָיו וַיָּקֻמוּ וַיֵּלְכוּ יַחְדָּו אֶל־בְּאֵר שָׁבַע וַיֵּשֶׁב אַבְרָהָם בִּבְאֵר שָׁבַע׃  וַיְהִי אַחֲרֵי הַדְּבָרִים הָאֵלֶּה וַיֻּגַּד לְאַבְרָהָם לֵאמֹר הִנֵּה יָלְדָה מִלְכָּה גַם־הִוא בָּנִים לְנָחוֹר אָחִיךָ׃  אֶת־עוּץ בְּכֹרוֹ וְאֶת־בּוּז אָחִיו וְאֶת־קְמוּאֵל אֲבִי אֲרָם׃  וְאֶת־כֶּשֶׂד וְאֶת־חֲזוֹ וְאֶת־פִּלְדָּשׁ וְאֶת־יִדְלָף וְאֵת בְּתוּאֵל׃  וּבְתוּאֵל יָלַד אֶת־רִבְקָה שְׁמֹנָה אֵלֶּה יָלְדָה מִלְכָּה לְנָחוֹר אֲחִי אַבְרָהָם׃  וּפִילַגְשׁוֹ וּשְׁמָהּ רְאוּמָה וַתֵּלֶד גַּם־הִוא אֶת־טֶבַח וְאֶת־גַּחַם וְאֶת־תַּחַשׁ וְאֶת־מַעֲכָה׃     וַיִּהְיוּ חַיֵּי שָׂרָה מֵאָה שָׁנָה וְעֶשְׂרִים שָׁנָה וְשֶׁבַע שָׁנִים שְׁנֵי חַיֵּי שָׂרָה׃  וַתָּמָת שָׂרָה בְּקִרְיַת אַרְבַּע הִוא חֶבְרוֹן בְּאֶרֶץ כְּנָעַן וַיָּבֹא אַבְרָהָם לִסְפֹּד לְשָׂרָה וְלִבְכֹּתָהּ׃  וַיָּקָם אַבְרָהָם מֵעַל פְּנֵי מֵתוֹ וַיְדַבֵּר אֶל־בְּנֵי־חֵת לֵאמֹר׃  גֵּר־וְתוֹשָׁב אָנֹכִי עִמָּכֶם תְּנוּ לִי אֲחֻזַּת־קֶבֶר עִמָּכֶם וְאֶקְבְּרָה מֵתִי מִלְּפָנָי׃  וַיַּעֲנוּ בְנֵי־חֵת אֶת־אַבְרָהָם לֵאמֹר לוֹ׃  שְׁמָעֵנוּ אֲדֹנִי נְשִׂיא אֱלֹהִים אַתָּה בְּתוֹכֵנוּ בְּמִבְחַר קְבָרֵינוּ קְבֹר אֶת־מֵתֶךָ אִישׁ מִמֶּנּוּ אֶת־קִבְרוֹ לֹא־יִכְלֶה מִמְּךָ מִקְּבֹר מֵתֶךָ׃  וַיָּקָם אַבְרָהָם וַיִּשְׁתַּחוּ לְעַם־הָאָרֶץ לִבְנֵי־חֵת׃  וַיְדַבֵּר אִתָּם לֵאמֹר אִם־יֵשׁ אֶת־נַפְשְׁכֶם לִקְבֹּר אֶת־מֵתִי מִלְּפָנַי שְׁמָעוּנִי וּפִגְעוּ־לִי בְּעֶפְרוֹן בֶּן־צֹחַר׃  וְיִתֶּן־לִי אֶת־מְעָרַת הַמַּכְפֵּלָה אֲשֶׁר־לוֹ אֲשֶׁר בִּקְצֵה שָׂדֵהוּ בְּכֶסֶף מָלֵא יִתְּנֶנָּה לִי בְּתוֹכְכֶם לַאֲחֻזַּת־קָבֶר׃  וְעֶפְרוֹן יֹשֵׁב בְּתוֹךְ בְּנֵי־חֵת וַיַּעַן עֶפְרוֹן הַחִתִּי אֶת־אַבְרָהָם בְּאָזְנֵי בְנֵי־חֵת לְכֹל בָּאֵי שַׁעַר־עִירוֹ לֵאמֹר׃  לֹא־אֲדֹנִי שְׁמָעֵנִי הַשָּׂדֶה נָתַתִּי לָךְ וְהַמְּעָרָה אֲשֶׁר־בּוֹ לְךָ נְתַתִּיהָ לְעֵינֵי בְנֵי־עַמִּי נְתַתִּיהָ לָּךְ קְבֹר מֵתֶךָ׃  וַיִּשְׁתַּחוּ אַבְרָהָם לִפְנֵי עַם הָאָרֶץ׃  וַיְדַבֵּר אֶל־עֶפְרוֹן בְּאָזְנֵי עַם־הָאָרֶץ לֵאמֹר אַךְ אִם־אַתָּה לוּ שְׁמָעֵנִי נָתַתִּי כֶּסֶף הַשָּׂדֶה קַח מִמֶּנִּי וְאֶקְבְּרָה אֶת־מֵתִי שָׁמָּה׃  וַיַּעַן עֶפְרוֹן אֶת־אַבְרָהָם לֵאמֹר לוֹ׃  אֲדֹנִי שְׁמָעֵנִי אֶרֶץ אַרְבַּע מֵאֹת שֶׁקֶל־כֶּסֶף בֵּינִי וּבֵינְךָ מַה־הִוא וְאֶת־מֵתְךָ קְבֹר׃  וַיִּשְׁמַע אַבְרָהָם אֶל־עֶפְרוֹן וַיִּשְׁקֹל אַבְרָהָם לְעֶפְרֹן אֶת־הַכֶּסֶף אֲשֶׁר דִּבֶּר בְּאָזְנֵי בְנֵי־חֵת אַרְבַּע מֵאוֹת שֶׁקֶל כֶּסֶף עֹבֵר לַסֹּחֵר׃  וַיָּקָם שְׂדֵה עֶפְרוֹן אֲשֶׁר בַּמַּכְפֵּלָה אֲשֶׁר לִפְנֵי מַמְרֵא הַשָּׂדֶה וְהַמְּעָרָה אֲשֶׁר־בּוֹ וְכָל־הָעֵץ אֲשֶׁר בַּשָּׂדֶה אֲשֶׁר בְּכָל־גְּבֻלוֹ סָבִיב׃  לְאַבְרָהָם לְמִקְנָה לְעֵינֵי בְנֵי־חֵת בְּכֹל בָּאֵי שַׁעַר־עִירוֹ׃  וְאַחֲרֵי־כֵן קָבַר אַבְרָהָם אֶת־שָׂרָה אִשְׁתּוֹ אֶל־מְעָרַת שְׂדֵה הַמַּכְפֵּלָה עַל־פְּנֵי מַמְרֵא הִוא חֶבְרוֹן בְּאֶרֶץ כְּנָעַן׃  וַיָּקָם הַשָּׂדֶה וְהַמְּעָרָה אֲשֶׁר־בּוֹ לְאַבְרָהָם לַאֲחֻזַּת־קָבֶר מֵאֵת בְּנֵי־חֵת׃     וְאַבְרָהָם זָקֵן בָּא בַּיָּמִים וַיהוָה בֵּרַךְ אֶת־אַבְרָהָם בַּכֹּל׃  וַיֹּאמֶר אַבְרָהָם אֶל־עַבְדּוֹ זְקַן בֵּיתוֹ הַמֹּשֵׁל בְּכָל־אֲשֶׁר־לוֹ שִׂים־נָא יָדְךָ תַּחַת יְרֵכִי׃  וְאַשְׁבִּיעֲךָ בַּיהוָה אֱלֹהֵי הַשָּׁמַיִם וֵאלֹהֵי הָאָרֶץ אֲשֶׁר לֹא־תִקַּח אִשָּׁה לִבְנִי מִבְּנוֹת הַכְּנַעֲנִי אֲשֶׁר אָנֹכִי יוֹשֵׁב בְּקִרְבּוֹ׃  כִּי אֶל־אַרְצִי וְאֶל־מוֹלַדְתִּי תֵּלֵךְ וְלָקַחְתָּ אִשָּׁה לִבְנִי לְיִצְחָק׃  וַיֹּאמֶר אֵלָיו הָעֶבֶד אוּלַי לֹא־תֹאבֶה הָאִשָּׁה לָלֶכֶת אַחֲרַי אֶל־הָאָרֶץ הַזֹּאת הֶהָשֵׁב אָשִׁיב אֶת־בִּנְךָ אֶל־הָאָרֶץ אֲשֶׁר־יָצָאתָ מִשָּׁם׃  וַיֹּאמֶר אֵלָיו אַבְרָהָם הִשָּׁמֶר לְךָ פֶּן־תָּשִׁיב אֶת־בְּנִי שָׁמָּה׃  יְהוָה אֱלֹהֵי הַשָּׁמַיִם אֲשֶׁר לְקָחַנִי מִבֵּית אָבִי וּמֵאֶרֶץ מוֹלַדְתִּי וַאֲשֶׁר דִּבֶּר־לִי וַאֲשֶׁר נִשְׁבַּע־לִי לֵאמֹר לְזַרְעֲךָ אֶתֵּן אֶת־הָאָרֶץ הַזֹּאת הוּא יִשְׁלַח מַלְאָכוֹ לְפָנֶיךָ וְלָקַחְתָּ אִשָּׁה לִבְנִי מִשָּׁם׃  וְאִם־לֹא תֹאבֶה הָאִשָּׁה לָלֶכֶת אַחֲרֶיךָ וְנִקִּיתָ מִשְּׁבֻעָתִי זֹאת רַק אֶת־בְּנִי לֹא תָשֵׁב שָׁמָּה׃  וַיָּשֶׂם הָעֶבֶד אֶת־יָדוֹ תַּחַת יֶרֶךְ אַבְרָהָם אֲדֹנָיו וַיִּשָּׁבַע לוֹ עַל־הַדָּבָר הַזֶּה׃  וַיִּקַּח הָעֶבֶד עֲשָׂרָה גְמַלִּים מִגְּמַלֵּי אֲדֹנָיו וַיֵּלֶךְ וְכָל־טוּב אֲדֹנָיו בְּיָדוֹ וַיָּקָם וַיֵּלֶךְ אֶל־אֲרַם נַהֲרַיִם אֶל־עִיר נָחוֹר׃  וַיַּבְרֵךְ הַגְּמַלִּים מִחוּץ לָעִיר אֶל־בְּאֵר הַמָּיִם לְעֵת עֶרֶב לְעֵת צֵאת הַשֹּׁאֲבֹת׃  וַיֹּאמַר יְהוָה אֱלֹהֵי אֲדֹנִי אַבְרָהָם הַקְרֵה־נָא לְפָנַי הַיּוֹם וַעֲשֵׂה־חֶסֶד עִם אֲדֹנִי אַבְרָהָם׃  הִנֵּה אָנֹכִי נִצָּב עַל־עֵין הַמָּיִם וּבְנוֹת אַנְשֵׁי הָעִיר יֹצְאֹת לִשְׁאֹב מָיִם׃  וְהָיָה הַנַּעֲרָ אֲשֶׁר אֹמַר אֵלֶיהָ הַטִּי־נָא כַדֵּךְ וְאֶשְׁתֶּה וְאָמְרָה שְׁתֵה וְגַם־גְּמַלֶּיךָ אַשְׁקֶה אֹתָהּ הֹכַחְתָּ לְעַבְדְּךָ לְיִצְחָק וּבָהּ אֵדַע כִּי־עָשִׂיתָ חֶסֶד עִם־אֲדֹנִי׃  וַיְהִי־הוּא טֶרֶם כִּלָּה לְדַבֵּר וְהִנֵּה רִבְקָה יֹצֵאת אֲשֶׁר יֻלְּדָה לִבְתוּאֵל בֶּן־מִלְכָּה אֵשֶׁת נָחוֹר אֲחִי אַבְרָהָם וְכַדָּהּ עַל־שִׁכְמָהּ׃  וְהַנַּעֲרָ טֹבַת מַרְאֶה מְאֹד בְּתוּלָה וְאִישׁ לֹא יְדָעָהּ וַתֵּרֶד הָעַיְנָה וַתְּמַלֵּא כַדָּהּ וַתָּעַל׃  וַיָּרָץ הָעֶבֶד לִקְרָאתָהּ וַיֹּאמֶר הַגְמִיאִינִי נָא מְעַט־מַיִם מִכַּדֵּךְ׃  וַתֹּאמֶר שְׁתֵה אֲדֹנִי וַתְּמַהֵר וַתֹּרֶד כַּדָּהּ עַל־יָדָהּ וַתַּשְׁקֵהוּ׃  וַתְּכַל לְהַשְׁקֹתוֹ וַתֹּאמֶר גַּם לִגְמַלֶּיךָ אֶשְׁאָב עַד אִם־כִּלּוּ לִשְׁתֹּת׃  וַתְּמַהֵר וַתְּעַר כַּדָּהּ אֶל־הַשֹּׁקֶת וַתָּרָץ עוֹד אֶל־הַבְּאֵר לִשְׁאֹב וַתִּשְׁאַב לְכָל־גְּמַלָּיו׃  וְהָאִישׁ מִשְׁתָּאֵה לָהּ מַחֲרִישׁ לָדַעַת הַהִצְלִיחַ יְהוָה דַּרְכּוֹ אִם־לֹא׃  וַיְהִי כַּאֲשֶׁר כִּלּוּ הַגְּמַלִּים לִשְׁתּוֹת וַיִּקַּח הָאִישׁ נֶזֶם זָהָב בֶּקַע מִשְׁקָלוֹ וּשְׁנֵי צְמִידִים עַל־יָדֶיהָ עֲשָׂרָה זָהָב מִשְׁקָלָם׃  וַיֹּאמֶר בַּת־מִי אַתְּ הַגִּידִי נָא לִי הֲיֵשׁ בֵּית־אָבִיךְ מָקוֹם לָנוּ לָלִין׃  וַתֹּאמֶר אֵלָיו בַּת־בְּתוּאֵל אָנֹכִי בֶּן־מִלְכָּה אֲשֶׁר יָלְדָה לְנָחוֹר׃  וַתֹּאמֶר אֵלָיו גַּם־תֶּבֶן גַּם־מִסְפּוֹא רַב עִמָּנוּ גַּם־מָקוֹם לָלוּן׃  וַיִּקֹּד הָאִישׁ וַיִּשְׁתַּחוּ לַיהוָה׃  וַיֹּאמֶר בָּרוּךְ יְהוָה אֱלֹהֵי אֲדֹנִי אַבְרָהָם אֲשֶׁר לֹא־עָזַב חַסְדּוֹ וַאֲמִתּוֹ מֵעִם אֲדֹנִי אָנֹכִי בַּדֶּרֶךְ נָחַנִי יְהוָה בֵּית אֲחֵי אֲדֹנִי׃  וַתָּרָץ הַנַּעֲרָ וַתַּגֵּד לְבֵית אִמָּהּ כַּדְּבָרִים הָאֵלֶּה׃  וּלְרִבְקָה אָח וּשְׁמוֹ לָבָן וַיָּרָץ לָבָן אֶל־הָאִישׁ הַחוּצָה אֶל־הָעָיִן׃  וַיְהִי כִּרְאֹת אֶת־הַנֶּזֶם וְאֶת־הַצְּמִדִים עַל־יְדֵי אֲחֹתוֹ וּכְשָׁמְעוֹ אֶת־דִּבְרֵי רִבְקָה אֲחֹתוֹ לֵאמֹר כֹּה־דִבֶּר אֵלַי הָאִישׁ וַיָּבֹא אֶל־הָאִישׁ וְהִנֵּה עֹמֵד עַל־הַגְּמַלִּים עַל־הָעָיִן׃  וַיֹּאמֶר בּוֹא בְּרוּךְ יְהוָה לָמָּה תַעֲמֹד בַּחוּץ וְאָנֹכִי פִּנִּיתִי הַבַּיִת וּמָקוֹם לַגְּמַלִּים׃  וַיָּבֹא הָאִישׁ הַבַּיְתָה וַיְפַתַּח הַגְּמַלִּים וַיִּתֵּן תֶּבֶן וּמִסְפּוֹא לַגְּמַלִּים וּמַיִם לִרְחֹץ רַגְלָיו וְרַגְלֵי הָאֲנָשִׁים אֲשֶׁר אִתּוֹ׃  ויישם  וַיּוּשַׂם  לְפָנָיו לֶאֱכֹל וַיֹּאמֶר לֹא אֹכַל עַד אִם־דִּבַּרְתִּי דְּבָרָי וַיֹּאמֶר דַּבֵּר׃  וַיֹּאמַר עֶבֶד אַבְרָהָם אָנֹכִי׃  וַיהוָה בֵּרַךְ אֶת־אֲדֹנִי מְאֹד וַיִּגְדָּל וַיִּתֶּן־לוֹ צֹאן וּבָקָר וְכֶסֶף וְזָהָב וַעֲבָדִם וּשְׁפָחֹת וּגְמַלִּים וַחֲמֹרִים׃  וַתֵּלֶד שָׂרָה אֵשֶׁת אֲדֹנִי בֵן לַאדֹנִי אַחֲרֵי זִקְנָתָהּ וַיִּתֶּן־לּוֹ אֶת־כָּל־אֲשֶׁר־לוֹ׃  וַיַּשְׁבִּעֵנִי אֲדֹנִי לֵאמֹר לֹא־תִקַּח אִשָּׁה לִבְנִי מִבְּנוֹת הַכְּנַעֲנִי אֲשֶׁר אָנֹכִי יֹשֵׁב בְּאַרְצוֹ׃  אִם־לֹא אֶל־בֵּית־אָבִי תֵּלֵךְ וְאֶל־מִשְׁפַּחְתִּי וְלָקַחְתָּ אִשָּׁה לִבְנִי׃  וָאֹמַר אֶל־אֲדֹנִי אֻלַי לֹא־תֵלֵךְ הָאִשָּׁה אַחֲרָי׃  וַיֹּאמֶר אֵלָי יְהוָה אֲשֶׁר־הִתְהַלַּכְתִּי לְפָנָיו יִשְׁלַח מַלְאָכוֹ אִתָּךְ וְהִצְלִיחַ דַּרְכֶּךָ וְלָקַחְתָּ אִשָּׁה לִבְנִי מִמִּשְׁפַּחְתִּי וּמִבֵּית אָבִי׃  אָז תִּנָּקֶה מֵאָלָתִי כִּי תָבוֹא אֶל־מִשְׁפַּחְתִּי וְאִם־לֹא יִתְּנוּ לָךְ וְהָיִיתָ נָקִי מֵאָלָתִי׃  וָאָבֹא הַיּוֹם אֶל־הָעָיִן וָאֹמַר יְהוָה אֱלֹהֵי אֲדֹנִי אַבְרָהָם אִם־יֶשְׁךָ־נָּא מַצְלִיחַ דַּרְכִּי אֲשֶׁר אָנֹכִי הֹלֵךְ עָלֶיהָ׃  הִנֵּה אָנֹכִי נִצָּב עַל־עֵין הַמָּיִם וְהָיָה הָעַלְמָה הַיֹּצֵאת לִשְׁאֹב וְאָמַרְתִּי אֵלֶיהָ הַשְׁקִינִי־נָא מְעַט־מַיִם מִכַּדֵּךְ׃  וְאָמְרָה אֵלַי גַּם־אַתָּה שְׁתֵה וְגַם לִגְמַלֶּיךָ אֶשְׁאָב הִוא הָאִשָּׁה אֲשֶׁר־הֹכִיחַ יְהוָה לְבֶן־אֲדֹנִי׃  אֲנִי טֶרֶם אֲכַלֶּה לְדַבֵּר אֶל־לִבִּי וְהִנֵּה רִבְקָה יֹצֵאת וְכַדָּהּ עַל־שִׁכְמָהּ וַתֵּרֶד הָעַיְנָה וַתִּשְׁאָב וָאֹמַר אֵלֶיהָ הַשְׁקִינִי נָא׃  וַתְּמַהֵר וַתּוֹרֶד כַּדָּהּ מֵעָלֶיהָ וַתֹּאמֶר שְׁתֵה וְגַם־גְּמַלֶּיךָ אַשְׁקֶה וָאֵשְׁתְּ וְגַם הַגְּמַלִּים הִשְׁקָתָה׃  וָאֶשְׁאַל אֹתָהּ וָאֹמַר בַּת־מִי אַתְּ וַתֹּאמֶר בַּת־בְּתוּאֵל בֶּן־נָחוֹר אֲשֶׁר יָלְדָה־לּוֹ מִלְכָּה וָאָשִׂם הַנֶּזֶם עַל־אַפָּהּ וְהַצְּמִידִים עַל־יָדֶיהָ׃  וָאֶקֹּד וָאֶשְׁתַּחֲוֶה לַיהוָה וָאֲבָרֵךְ אֶת־יְהוָה אֱלֹהֵי אֲדֹנִי אַבְרָהָם אֲשֶׁר הִנְחַנִי בְּדֶרֶךְ אֱמֶת לָקַחַת אֶת־בַּת־אֲחִי אֲדֹנִי לִבְנוֹ׃  וְעַתָּה אִם־יֶשְׁכֶם עֹשִׂים חֶסֶד וֶאֱמֶת אֶת־אֲדֹנִי הַגִּידוּ לִי וְאִם־לֹא הַגִּידוּ לִי וְאֶפְנֶה עַל־יָמִין אוֹ עַל־שְׂמֹאל׃  וַיַּעַן לָבָן וּבְתוּאֵל וַיֹּאמְרוּ מֵיְהוָה יָצָא הַדָּבָר לֹא נוּכַל דַּבֵּר אֵלֶיךָ רַע אוֹ־טוֹב׃  הִנֵּה־רִבְקָה לְפָנֶיךָ קַח וָלֵךְ וּתְהִי אִשָּׁה לְבֶן־אֲדֹנֶיךָ כַּאֲשֶׁר דִּבֶּר יְהוָה׃  וַיְהִי כַּאֲשֶׁר שָׁמַע עֶבֶד אַבְרָהָם אֶת־דִּבְרֵיהֶם וַיִּשְׁתַּחוּ אַרְצָה לַיהוָה׃  וַיּוֹצֵא הָעֶבֶד כְּלֵי־כֶסֶף וּכְלֵי זָהָב וּבְגָדִים וַיִּתֵּן לְרִבְקָה וּמִגְדָּנֹת נָתַן לְאָחִיהָ וּלְאִמָּהּ׃  וַיֹּאכְלוּ וַיִּשְׁתּוּ הוּא וְהָאֲנָשִׁים אֲשֶׁר־עִמּוֹ וַיָּלִינוּ וַיָּקוּמוּ בַבֹּקֶר וַיֹּאמֶר שַׁלְּחֻנִי לַאדֹנִי׃  וַיֹּאמֶר אָחִיהָ וְאִמָּהּ תֵּשֵׁב הַנַּעֲרָ אִתָּנוּ יָמִים אוֹ עָשׂוֹר אַחַר תֵּלֵךְ׃  וַיֹּאמֶר אֲלֵהֶם אַל־תְּאַחֲרוּ אֹתִי וַיהוָה הִצְלִיחַ דַּרְכִּי שַׁלְּחוּנִי וְאֵלְכָה לַאדֹנִי׃  וַיֹּאמְרוּ נִקְרָא לַנַּעֲרָ וְנִשְׁאֲלָה אֶת־פִּיהָ׃  וַיִּקְרְאוּ לְרִבְקָה וַיֹּאמְרוּ אֵלֶיהָ הֲתֵלְכִי עִם־הָאִישׁ הַזֶּה וַתֹּאמֶר אֵלֵךְ׃  וַיְשַׁלְּחוּ אֶת־רִבְקָה אֲחֹתָם וְאֶת־מֵנִקְתָּהּ וְאֶת־עֶבֶד אַבְרָהָם וְאֶת־אֲנָשָׁיו׃  וַיְבָרֲכוּ אֶת־רִבְקָה וַיֹּאמְרוּ`

let words = text.split(/\s+/)

const MAXWIDTH = 500

measurer = document.createElement('span')
measurer.setAttribute('id', 'Test')
document.body.appendChild(measurer)

start()

async function start() {
  let line = []
  let lines = [line]

  for (let i = 0, n = words.length; i < n; i++) {
    await appendToLines(lines, words[i], ' ', 1.0)
  }

  var result = []

  lines.forEach(line => {
    result.push(`<div>${line.join(line.joiner)}</div>`)
  })

  document.querySelector('#box').innerHTML = result.join('\n')
}

async function appendToLines(lines, next, join, grow = 1.1) {
  return new Promise((res, rej) => {
    let line = lines[lines.length - 1]
    line.joiner = join
    let text = line.concat([next]).join(join)
    const size = measure(text, function(size){
      if (size * grow >= MAXWIDTH) {
        if (line[line.length - 1].match('class="s')) {
          line = [line.pop(), next]
        } else {
          line = [next]
        }
        lines.push(line)
      } else {
        line.push(next)
      }
      res()
    })
  })
}

function measure(string, fn) {
  measurer.innerHTML = string
  var a = measurer.offsetWidth
  setTimeout(function(){
    var b = measurer.offsetWidth
    fn(b)
  }, 1)
}
#Test {
  position: absolute;
  height: auto;
  width: auto;
  white-space: nowrap; /* Thanks to Herb Caudill comment */
}

#box {
  width: 500px;
  height: 500px;
  background: red;
}
<div id="box">

</div>

You'll notice the text is rapidly being appended to the DOM, measured, waited for a timeout, then measured again, and when its finally done, it lays out the text. Each line should have a calculated width.

But what happens is that the measured text is larger than the MAXWIDTH. Check out the rendered text, in places it overflows to the next line. I had to add a grow parameter as a multiplier to get it to work reasonably. With that factor, it measures "oh this plus some buffer is longer than MAXWIDTH, so create a new line". It doesn't work without that buffer.

To make it worse, if you remove the asynchronousness of the measure function, and make it synchronous, it will give even worse results. Why is this? Does it depend on the font I'm using? I am using the Noto fonts if that matters.

How do I properly calculate the width of the text in advance, so I can calculate individual lines without it getting wrapped around?

Lance
  • 75,200
  • 93
  • 289
  • 503

1 Answers1

1

I had a similar problem in one of my applications and I researched measuring text quite a lot.

There are only 2 ways to do it that are really compatible with common browsers:

  1. The way you do it by measuring an HTML element: This is a very bad way to do it because it forces reflow/layout everytime you call offsetWidth or a similar function (see https://gist.github.com/paulirish/5d52fb081b3570c81e3a). That might also be why you have problems with your code (Your example actually works for me, or I don't really understand what is not working from the example).

  2. The other way to do it (which I prefer) is using the measureText method of the canvas 2d context (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText). The Problem here is, that the measureText() method cannot calculate line breaks (it will use \n if present but cannot break lines by itself). You will have to do it yourself. This also has the problem that the canvas measureText().width gives slightly different values than the actual width of html elements with the same text. Modern browsers support .actualBoundingBoxRight instead of .width which is more accurate but still not perfect. To adjust for that, you will have to calculate with a slightly different maxWidth then actually required (in my case higher). If you don't care about text selection, you could also just render the text directly to the canvas, then the calculations will be exact.

I assume from your code that you want to break whole words. This is a simple example that does not account for the problem when a single word is actually wider than the desired width. You will need to make adjustments for that if that is a possible problem.

Performance can also be optimized depending on the usecase. I measure individual words here which might not be optimal if there are a lot of words per line. You could also estimate the word / character count per line and then try from there. Like: estimated words per line is 10, so try measuring the first 10 words, if it is > maxWidth, take one word (or multiple if the offset is large) away and measure again. If it is < maxWidth add one word (or multiple) to see if more words would fit the line.

const text = "וַיּוֹצֵא הָעֶבֶד כְּלֵי־כֶסֶף וּכְלֵי זָהָב וּבְגָדִים וַיִּתֵּן לְרִבְקָה וּמִגְדָּנֹת נָתַן לְאָחִיהָ וּלְאִמָּהּ׃  וַיֹּאכְלוּ וַיִּשְׁתּוּ הוּא וְהָאֲנָשִׁים אֲשֶׁר־עִמּוֹ וַיָּלִינוּ וַיָּקוּמוּ בַבֹּקֶר וַיֹּאמֶר שַׁלְּחֻנִי לַאדֹנִי׃  וַיֹּאמֶר אָחִיהָ וְאִמָּהּ תֵּשֵׁב הַנַּעֲרָ אִתָּנוּ יָמִים אוֹ עָשׂוֹר אַחַר תֵּלֵךְ׃  וַיֹּאמֶר אֲלֵהֶם אַל־תְּאַחֲרוּ אֹתִי וַיהוָה הִצְלִיחַ דַּרְכִּי שַׁלְּחוּנִי וְאֵלְכָה לַאדֹנִי׃  וַיֹּאמְרוּ נִקְרָא לַנַּעֲרָ וְנִשְׁאֲלָה אֶת־פִּיהָ׃  וַיִּקְרְאוּ לְרִבְקָה וַיֹּאמְרוּ אֵלֶיהָ הֲתֵלְכִי עִם־הָאִישׁ הַזֶּה וַתֹּאמֶר אֵלֵךְ׃  וַיְשַׁלְּחוּ אֶת־רִבְקָה אֲחֹתָם וְאֶת־מֵנִקְתָּהּ וְאֶת־עֶבֶד אַבְרָהָם וְאֶת־אֲנָשָׁיו׃  וַיְבָרֲכוּ אֶת־רִבְקָה וַיֹּאמְרוּ";
const words = text.split(/\s+/);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
// font size, style and family of context and target html element must be the same
ctx.font = "15px sans-serif";
document.body.style = "font-size: 15px; font-family: sans-serif";
const spaceWidth = ctx.measureText(" ").width
// the measure text seems to slightly differs from how the browser actually renders the text outside a canvas. using actualBoundingBoxRight instead of width gives better results but is not supported by any IE. see https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
const wordWidths = words.map(w => ctx.measureText(w).width);
// const wordWidths = words.map(w => ctx.measureText(w).actualBoundingBoxRight);

let currentLine = '';
let currentWidth = 0;
let line = 0;
const maxWidth = 300;
words.forEach((word, i) => {
  let width = wordWidths[i];
  if (currentWidth > 0) width += spaceWidth;
  // this goes wrong if a single word has a width > maxWidth -> can be broken up hear by measuring the 
  if (currentWidth + width > maxWidth) {
    // console.log(currentWidth); for reference, this differs slightly from the actual rendered content
    appendLine(currentLine, line);
    currentLine = '';
    currentWidth = 0;
    line++;
  }
  if(currentWidth > 0) currentLine += ' ';
  currentLine += word;
  currentWidth += width;
});
if(currentWidth > 0) appendLine(currentLine, line);

function appendLine(text, index) {
  const div = document.createElement("div");
  div.innerText = text;
  // for testing a line height of 20 is used here, but it could also be calculated using the font-size
  div.style = `position:absolute;top:${index * 20}px;left:0`;
  document.body.appendChild(div);
}
x4rf41
  • 5,184
  • 2
  • 22
  • 33
  • Another problem I have is, I am using words with custom classes on _some_ words, so I need to be able to measure the text with various intermingled classes (and font sizes), not sure how to do that accurately given how spaces between font sizes might be rendered... – Lance Jul 14 '20 at 01:12
  • When you have varying formatting the text, then it will be harder with canvas, you need to essentially parse the html und use the style in the canvas context. Which can be easy when you only have some highlighting (like some word being bold, underline, etc) but can get really problematic because quite a few css features are not supported or implemented differently by the canvas. – x4rf41 Jul 14 '20 at 09:31
  • Using an element and outerWidth should work as well though. Your example works fine for me (both async and sync) and it also should according to specs. Maybe you can provide a screenshot that highlights the error. The only problems with the element approach are a) performance (which might not be an issue for you) and b) I think you need to ensure that the font is loaded before measuring (potential ways of doing so can be found here https://stackoverflow.com/questions/12312323/how-to-know-if-a-font-font-face-has-already-been-loaded . If you search you will find more on the issue as well) – x4rf41 Jul 14 '20 at 09:39