Aha, _that's_ why paragraphs weren't properly justified. Confusion
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Tue, 13 Apr 2004 14:14:12 +0000 (14:14 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Tue, 13 Apr 2004 14:14:12 +0000 (14:14 +0000)
of semantics as to whether a `last' pointer pointed to the last
relevant thing in a list, or the one beyond that. Oops.

git-svn-id: svn://svn.tartarus.org/sgt/halibut@4059 cda61777-01e9-0310-a592-d414129be87e

bk_paper.c
paper.h

index 0bf3ce1..aff3975 100644 (file)
 /*
  * To be done:
  * 
- *  - Text wrapping is suspicious in both PS and PDF: the space
- *    adjust seems to be _approximately_ working, but not exactly.
- *    I bet some rounding error compensation is required.
- * 
  *  - set up contents section now we know what sections begin on
  *    which pages
  * 
@@ -385,25 +381,23 @@ static int string_width(font_data *font, wchar_t const *string, int *errs)
     return width;
 }
 
-static int paper_width(void *vctx, word *word);
+static int paper_width_internal(void *vctx, word *word, int *nspaces);
 
 struct paper_width_ctx {
     int minspacewidth;
     para_data *pdata;
 };
 
-static int paper_width_list(void *vctx, word *text, word *end) {
+static int paper_width_list(void *vctx, word *text, word *end, int *nspaces) {
     int w = 0;
-    while (text) {
-       w += paper_width(vctx, text);
-       if (text == end)
-           break;
+    while (text && text != end) {
+       w += paper_width_internal(vctx, text, nspaces);
        text = text->next;
     }
     return w;
 }
 
-static int paper_width(void *vctx, word *word)
+static int paper_width_internal(void *vctx, word *word, int *nspaces)
 {
     struct paper_width_ctx *ctx = (struct paper_width_ctx *)vctx;
     int style, type, findex, width, errs;
@@ -429,9 +423,11 @@ static int paper_width(void *vctx, word *word)
     if (type == word_Normal) {
        str = word->text;
     } else if (type == word_WhiteSpace) {
-       if (findex != FONT_CODE)
+       if (findex != FONT_CODE) {
+           if (nspaces)
+               (*nspaces)++;
            return ctx->minspacewidth;
-       else
+       else
            str = L" ";
     } else /* if (type == word_Quote) */ {
        if (word->aux == quote_Open)
@@ -443,11 +439,16 @@ static int paper_width(void *vctx, word *word)
     width = string_width(ctx->pdata->fonts[findex], str, &errs);
 
     if (errs && word->alt)
-       return paper_width_list(vctx, word->alt, NULL);
+       return paper_width_list(vctx, word->alt, NULL, nspaces);
     else
        return ctx->pdata->sizes[findex] * width;
 }
 
+static int paper_width(void *vctx, word *word)
+{
+    return paper_width_internal(vctx, word, NULL);
+}
+
 static void wrap_paragraph(para_data *pdata, word *words,
                           int w, int i1, int i2)
 {
@@ -504,7 +505,7 @@ static void wrap_paragraph(para_data *pdata, word *words,
 
        ldata->pdata = pdata;
        ldata->first = p->begin;
-       ldata->last = p->end;
+       ldata->end = p->end;
        ldata->line_height = line_height;
 
        ldata->xpos = (p == wrapping ? i1 : i2);
@@ -519,57 +520,27 @@ static void wrap_paragraph(para_data *pdata, word *words,
        ldata->next = NULL;
        pdata->last = ldata;
 
-       len = paper_width_list(&ctx, ldata->first, ldata->last);
-       wid = (p == wrapping ? w - i1 : w - i2);
        spaces = 0;
+       len = paper_width_list(&ctx, ldata->first, ldata->end, &spaces);
+       wid = (p == wrapping ? w - i1 : w - i2);
        wd = ldata->first;
-       while (wd) {
-#if 0
-           switch (wd->type) {
-             case word_HyperLink:
-             case word_HyperEnd:
-             case word_UpperXref:
-             case word_LowerXref:
-             case word_XrefEnd:
-             case word_IndexRef:
-               break;
 
-             default:
-               if (removeattr(wd->type) == word_Normal)
-                   printf("%ls", wd->text);
-               else if (removeattr(wd->type) == word_WhiteSpace)
-                   printf(" ");
-               else if (removeattr(wd->type) == word_Quote)
-                   printf(wd->aux == quote_Open ? "`" : "'");
-               break;
-           }
-#endif
-           if (removeattr(wd->type) == word_WhiteSpace)
-               spaces++;
-           if (wd == ldata->last)
-               break;
-           wd = wd->next;
-       }
-
-       if (spaces) {
-           ldata->space_adjust = (wid - len) / spaces;
-           /*
-            * This tells us how much the space width needs to
-            * change from _min_spacewidth. But we want to store
-            * its difference from the _natural_ space width, to
-            * make the text rendering easier.
-            */
-           ldata->space_adjust += ctx.minspacewidth;
-           ldata->space_adjust -= spacewidth;
-           /*
-            * Special case: on the last line of a paragraph, we
-            * never stretch spaces.
-            */
-           if (ldata->space_adjust > 0 && !p->next)
-               ldata->space_adjust = 0;
-       } else {
-           ldata->space_adjust = 0;
-       }
+       ldata->hshortfall = wid - len;
+       ldata->nspaces = spaces;
+       /*
+        * This tells us how much the space width needs to
+        * change from _min_spacewidth. But we want to store
+        * its difference from the _natural_ space width, to
+        * make the text rendering easier.
+        */
+       ldata->hshortfall += ctx.minspacewidth * spaces;
+       ldata->hshortfall -= spacewidth * spaces;
+       /*
+        * Special case: on the last line of a paragraph, we
+        * never stretch spaces.
+        */
+       if (ldata->hshortfall > 0 && !p->next)
+           ldata->hshortfall = 0;
 
        ldata->aux_text = NULL;
        ldata->aux_left_indent = 0;
@@ -654,9 +625,9 @@ static page_data *page_breaks(line_data *first, line_data *last,
                 */
                l->bestcost = cost;
                if (m->next && !m->next->page_break)
-                   l->shortfall = page_height - minheight;
+                   l->vshortfall = page_height - minheight;
                else
-                   l->shortfall = 0;
+                   l->vshortfall = 0;
                l->text = text;
                l->space = space;
                l->page_last = m;
@@ -702,7 +673,7 @@ static page_data *page_breaks(line_data *first, line_data *last,
 
            l->page = page;
            l->ypos = text + space +
-               space * (float)page->first_line->shortfall /
+               space * (float)page->first_line->vshortfall /
                page->first_line->space;
 
            if (l == page->last_line)
@@ -813,9 +784,10 @@ static int render_string(page_data *page, font_data *font, int fontsize,
  * Returns the updated x coordinate.
  */
 static int render_text(page_data *page, para_data *pdata, int x, int y,
-                      word *text, word *text_end, int space_adjust)
+                      word *text, word *text_end,
+                      int shortfall, int nspaces, int *nspace)
 {
-    while (text) {
+    while (text && text != text_end) {
        int style, type, findex, errs;
        wchar_t *str;
 
@@ -847,7 +819,11 @@ static int render_text(page_data *page, para_data *pdata, int x, int y,
        } else if (type == word_WhiteSpace) {
            x += pdata->sizes[findex] *
                string_width(pdata->fonts[findex], L" ", NULL);
-           x += space_adjust;
+           if (nspaces && findex != FONT_CODE) {
+               x += (*nspace+1) * shortfall / nspaces;
+               x -= *nspace * shortfall / nspaces;
+               (*nspace)++;
+           }
            goto nextword;
        } else /* if (type == word_Quote) */ {
            if (text->aux == quote_Open)
@@ -859,14 +835,13 @@ static int render_text(page_data *page, para_data *pdata, int x, int y,
        (void) string_width(pdata->fonts[findex], str, &errs);
 
        if (errs && text->alt)
-           x = render_text(page, pdata, x, y, text->alt, NULL, space_adjust);
+           x = render_text(page, pdata, x, y, text->alt, NULL,
+                           shortfall, nspaces, nspace);
        else
            x = render_string(page, pdata->fonts[findex],
                              pdata->sizes[findex], x, y, str);
 
        nextword:
-       if (text == text_end)
-           break;
        text = text->next;
     }
 
@@ -875,10 +850,14 @@ static int render_text(page_data *page, para_data *pdata, int x, int y,
 
 static void render_line(line_data *ldata, int left_x, int top_y)
 {
-    if (ldata->aux_text)
+    int nspace;
+    if (ldata->aux_text) {
+       nspace = 0;
        render_text(ldata->page, ldata->pdata, left_x + ldata->aux_left_indent,
-                   top_y - ldata->ypos, ldata->aux_text, NULL, 0);
+                   top_y - ldata->ypos, ldata->aux_text, NULL, 0, 0, &nspace);
+    }
+    nspace = 0;
     render_text(ldata->page, ldata->pdata, left_x + ldata->xpos,
-               top_y - ldata->ypos, ldata->first, ldata->last,
-               ldata->space_adjust);
+               top_y - ldata->ypos, ldata->first, ldata->end,
+               ldata->hshortfall, ldata->nspaces, &nspace);
 }
diff --git a/paper.h b/paper.h
index 6a4e353..3b4a944 100644 (file)
--- a/paper.h
+++ b/paper.h
@@ -151,12 +151,15 @@ struct line_data_Tag {
      * its starting x position, and by how much the width of spaces
      * needs to be adjusted for paragraph justification.
      * 
-     * (`last' may be NULL if it's more convenient.)
+     * (Unlike most of the `last' pointers defined in this file,
+     * this `end' pointer points to the word _after_ the last one
+     * that should be displayed on the line. This is how it's
+     * returned from wrap_para().)
      */
     word *first;
-    word *last;
+    word *end;
     int xpos;
-    int space_adjust;                 /* for justifying paragraphs */
+    int hshortfall, nspaces;          /* for justifying paragraphs */
     /*
      * Auxiliary text: a section number in a margin, or a list item
      * bullet or number. Also mention where to display this text
@@ -179,7 +182,7 @@ struct line_data_Tag {
      * These fields are used in the page breaking algorithm.
      */
     int bestcost;
-    int shortfall, text, space;
+    int vshortfall, text, space;
     line_data *page_last;             /* last line on a page starting here */
     /*
      * After page breaking, we can assign an actual y-coordinate on