diff options
45 files changed, 701 insertions, 361 deletions
@@ -12,6 +12,7 @@ dillo-3.0.3 [not released yet] +- Support for CSS display property - Replace polling in DNS with a pipe. - Packed trie to optimize hyphenator memory consumption. + - Fix crash in datauri dpi. Patches: Johannes Hofmann +- Fix image input coordinates (BUG#1070) - When location bar is given focus, temporarily show panels if hidden @@ -21,6 +22,7 @@ dillo-3.0.3 [not released yet] - Add some more info to various bug meter messages. - For text selection, fix releasing mouse button outside of boundary. - While selecting text, moving cursor outside viewport will scroll it. + - Make form resetting work for <select>. Patches: corvid +- Automatic hyphenation. Patch: Sebastian Geerken diff --git a/doc/dw-line-breaking.doc b/doc/dw-line-breaking.doc index 2967e98f..8d9a1df1 100644 --- a/doc/dw-line-breaking.doc +++ b/doc/dw-line-breaking.doc @@ -322,35 +322,11 @@ Bugs and Things Needing Improvement High Priority ------------- -**Bugs in hyphenation:** There seem to be problems when breaking -words containing hyphens already. Example: "Abtei-Stadt", which is -divided into "Abtei-" and "Stadt", resulting possibly in -"Abtei-<span></span>-[new line]Stadt". See also below under -"Medium Priority", on how to deal with hyphens and dashes. +None. Medium Priority --------------- -**Break hyphens and dashes:** The following rules seem to be relevant: - -- In English, an em-dash is used with no spaces around. Breaking - before and after the dash should be possible, perhaps with a - penalty > 0. (In German, an en-dash (Halbgeviert) with spaces around - is used instead.) -- After a hyphen, which is part of a compound word, a break should be - possible. As described above ("Abtei-Stadt"), this collides with - hyphenation. - -Where to implement? In the same dynamic, lazy way like hyphenation? As -part of hyphenation? - -Notice that Liang's algorithm may behave different regarding hyphens: -"Abtei-Stadt" is (using the patterns from CTAN) divided into "Abtei-" -and "Stadt", but "Nordrhein-Westfalen" is divided into "Nord", -"rhein-West", "fa", "len": the part containing the hyphen -("rhein-West") is untouched. (Sorry for the German words; if you have -got English examples, send them me.) - **Incorrect calculation of extremes:** The minimal width of a text block (as part of the width extremes, which are mainly used for tables) is defined by everything between two possible breaks. A @@ -410,8 +386,53 @@ lines will, when spaces are shrunken, get too long!) Analogous considerations must be made for right-aligned and centered text. (For centered texts, there are two adjustable spaces.) -**Hyphens in adjacent lines:** It should be simple to assign a larger +Solved (Must Be Documented) +--------------------------- + +These have been solved recently and should be documented above. + +*Bugs in hyphenation:* There seem to be problems when breaking words +containing hyphens already. Example: "Abtei-Stadt", which is divided +into "Abtei-" and "Stadt", resulting possibly in +"Abtei-<span></span>-[new line]Stadt". See also below under +"Medium Priority", on how to deal with hyphens and dashes. + +**Solution:** See next. + +*Break hyphens and dashes:* The following rules seem to be relevant: + +- In English, an em-dash is used with no spaces around. Breaking + before and after the dash should be possible, perhaps with a + penalty > 0. (In German, an en-dash (Halbgeviert) with spaces around + is used instead.) +- After a hyphen, which is part of a compound word, a break should be + possible. As described above ("Abtei-Stadt"), this collides with + hyphenation. + +Where to implement? In the same dynamic, lazy way like hyphenation? As +part of hyphenation? + +Notice that Liang's algorithm may behave different regarding hyphens: +"Abtei-Stadt" is (using the patterns from CTAN) divided into "Abtei-" +and "Stadt", but "Nordrhein-Westfalen" is divided into "Nord", +"rhein-West", "fa", "len": the part containing the hyphen +("rhein-West") is untouched. (Sorry for the German words; if you have +got English examples, send them me.)</div> + +**Solution for both:** This has been implemented in +dw::Textblock::addText, in a similar way to soft hyphens. Liang's +algorithm now only operates on the parts: "Abtei" and "Stadt"; +"Nordrhein" and "Westfalen". + +*Hyphens in adjacent lines:* It should be simple to assign a larger penalty for hyphens, when the line before is already hyphenated. This way, hyphens in adjacent lines are penalized further. +**Solved:** There are always two penalties. Must be documented in +detail. + +**Also:** + +- Configuration of penalties. + */ diff --git a/dpi/datauri.c b/dpi/datauri.c index 055ecd9f..0e3560ec 100644 --- a/dpi/datauri.c +++ b/dpi/datauri.c @@ -150,8 +150,8 @@ char *a_Url_decode_hex_str(const char *str, size_t *p_sz) } *dest = 0; - new_str = dRealloc(new_str, sizeof(char) * (dest - new_str + 1)); *p_sz = (size_t)(dest - new_str); + new_str = dRealloc(new_str, sizeof(char) * (dest - new_str + 1)); return new_str; } diff --git a/dpi/downloads.cc b/dpi/downloads.cc index 418dbd1b..a25b511e 100644 --- a/dpi/downloads.cc +++ b/dpi/downloads.cc @@ -30,6 +30,7 @@ #include <sys/wait.h> #include <FL/Fl.H> +#include <FL/fl_ask.H> #include <FL/fl_draw.H> #include <FL/Fl_File_Chooser.H> #include <FL/Fl_Window.H> diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc index d8e95a6e..7dd87a18 100644 --- a/dw/fltkplatform.cc +++ b/dw/fltkplatform.cc @@ -419,11 +419,11 @@ FltkPlatform::FltkResourceFactory::createOptionMenuResource () } core::ui::EntryResource * -FltkPlatform::FltkResourceFactory::createEntryResource (int maxLength, +FltkPlatform::FltkResourceFactory::createEntryResource (int size, bool password, const char *label) { - return new ui::FltkEntryResource (platform, maxLength, password, label); + return new ui::FltkEntryResource (platform, size, password, label); } core::ui::MultiLineTextResource * diff --git a/dw/fltkplatform.hh b/dw/fltkplatform.hh index 7b4272eb..64605b68 100644 --- a/dw/fltkplatform.hh +++ b/dw/fltkplatform.hh @@ -109,8 +109,7 @@ private: createListResource (core::ui::ListResource::SelectionMode selectionMode, int rows); core::ui::OptionMenuResource *createOptionMenuResource (); - core::ui::EntryResource *createEntryResource (int maxLength, - bool password, + core::ui::EntryResource *createEntryResource (int size, bool password, const char *label); core::ui::MultiLineTextResource *createMultiLineTextResource (int cols, int rows); diff --git a/dw/fltkui.cc b/dw/fltkui.cc index 5e4f3c56..f12306fa 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -536,11 +536,11 @@ Fl_Widget *FltkComplexButtonResource::createNewWidget (core::Allocation // ---------------------------------------------------------------------- -FltkEntryResource::FltkEntryResource (FltkPlatform *platform, int maxLength, +FltkEntryResource::FltkEntryResource (FltkPlatform *platform, int size, bool password, const char *label): FltkSpecificResource <dw::core::ui::EntryResource> (platform) { - this->maxLength = maxLength; + this->size = size; this->password = password; this->label = label ? strdup(label) : NULL; this->label_w = 0; @@ -615,7 +615,7 @@ void FltkEntryResource::sizeRequest (core::Requisition *requisition) // 1.3.0 (STR #2688). requisition->width = (int)fl_width ("n") - * (maxLength == UNLIMITED_MAX_LENGTH ? 10 : maxLength) + * (size == UNLIMITED_SIZE ? 10 : size) + label_w + (2 * RELIEF_X_THICKNESS); requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; requisition->descent = font->descent + RELIEF_Y_THICKNESS; @@ -670,6 +670,11 @@ void FltkEntryResource::setEditable (bool editable) this->editable = editable; } +void FltkEntryResource::setMaxLength (int maxlen) +{ + ((Fl_Input *)widget)->maximum_size(maxlen); +} + // ---------------------------------------------------------------------- FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform, @@ -1131,6 +1136,12 @@ void FltkOptionMenuResource::addItem (const char *str, queueResize (true); } +void FltkOptionMenuResource::setItem (int index, bool selected) +{ + if (selected) + ((Fl_Choice *)widget)->value(menu+index); +} + void FltkOptionMenuResource::pushGroup (const char *name, bool enabled) { Fl_Menu_Item *item = newItem(); @@ -1216,12 +1227,19 @@ void FltkListResource::widgetCallback (Fl_Widget *widget, void *data) { Fl_Tree_Item *fltkItem = ((Fl_Tree *) widget)->callback_item (); int index = -1; + if (fltkItem) index = (long) (fltkItem->user_data ()); if (index > -1) { - FltkListResource *res = (FltkListResource *) data; bool selected = fltkItem->is_selected (); - res->itemsSelected.set (index, selected); + + if (selected && fltkItem->has_children()) { + /* Don't permit a group to be selected. */ + fltkItem->deselect(); + } else { + FltkListResource *res = (FltkListResource *) data; + res->itemsSelected.set (index, selected); + } } } @@ -1257,11 +1275,35 @@ void FltkListResource::addItem (const char *str, bool enabled, bool selected) queueResize (true); } +void FltkListResource::setItem (int index, bool selected) +{ + Fl_Tree *tree = (Fl_Tree *) widget; + Fl_Tree_Item *item = tree->root()->next(); + + for (int i = 0; item && i < index; i++) + item = item->next(); + + if (item) { + bool do_callback = false; + itemsSelected.set (index, selected); + if (selected) { + if (mode == SELECTION_MULTIPLE) { + tree->select(item, do_callback); + } else { + /* callback to deselect other selected item */ + do_callback = true; + tree->select_only(item, do_callback); + } + } else { + tree->deselect(item, do_callback); + } + } +} + void FltkListResource::pushGroup (const char *name, bool enabled) { bool selected = false; - /* TODO: make it impossible to select a group */ currParent = (Fl_Tree_Item *) newItem(name, enabled, selected); queueResize (true); } diff --git a/dw/fltkui.hh b/dw/fltkui.hh index ff927c80..e1845abd 100644 --- a/dw/fltkui.hh +++ b/dw/fltkui.hh @@ -282,7 +282,7 @@ class FltkEntryResource: public FltkSpecificResource <dw::core::ui::EntryResource> { private: - int maxLength; + int size; bool password; const char *initText; char *label; @@ -297,7 +297,7 @@ protected: void setWidgetStyle (Fl_Widget *widget, core::style::Style *style); public: - FltkEntryResource (FltkPlatform *platform, int maxLength, bool password, + FltkEntryResource (FltkPlatform *platform, int size, bool password, const char *label); ~FltkEntryResource (); @@ -308,6 +308,7 @@ public: void setText (const char *text); bool isEditable (); void setEditable (bool editable); + void setMaxLength (int maxlen); }; @@ -445,6 +446,7 @@ template <class I> class FltkSelectionResource: protected: virtual bool setSelectedItems() { return false; } virtual void addItem (const char *str, bool enabled, bool selected) = 0; + virtual void setItem (int index, bool selected) = 0; virtual void pushGroup (const char *name, bool enabled) = 0; virtual void popGroup () = 0; public: @@ -475,6 +477,7 @@ public: ~FltkOptionMenuResource (); void addItem (const char *str, bool enabled, bool selected); + void setItem (int index, bool selected); void pushGroup (const char *name, bool enabled); void popGroup (); @@ -506,6 +509,7 @@ public: ~FltkListResource (); void addItem (const char *str, bool enabled, bool selected); + void setItem (int index, bool selected); void pushGroup (const char *name, bool enabled); void popGroup (); diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc index d9782a4e..0977aac5 100644 --- a/dw/fltkviewbase.cc +++ b/dw/fltkviewbase.cc @@ -538,6 +538,11 @@ void FltkWidgetView::drawText (core::style::Font *font, core::style::Color::Shading shading, int X, int Y, const char *text, int len) { + //printf ("drawText (..., %d, %d, '", X, Y); + //for (int i = 0; i < len; i++) + // putchar (text[i]); + //printf ("'\n"); + FltkFont *ff = (FltkFont*)font; fl_font(ff->font, ff->size); fl_color(((FltkColor*)color)->colors[shading]); diff --git a/dw/style.hh b/dw/style.hh index 6b492793..0df5c5e0 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -544,7 +544,7 @@ protected: void copyAttrs (StyleAttrs *attrs); public: - inline static Style *create (Layout *layout, StyleAttrs *attrs) + inline static Style *create (StyleAttrs *attrs) { Style *style = styleTable->get (attrs); if (style) { diff --git a/dw/textblock.cc b/dw/textblock.cc index 1fbf0032..5d43f653 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -34,13 +34,86 @@ static dw::core::style::Tooltip *hoverTooltip = NULL; - using namespace lout; namespace dw { int Textblock::CLASS_ID = -1; +Textblock::DivChar Textblock::divChars[NUM_DIV_CHARS] = { + // soft hyphen (U+00AD) + { "\xc2\xad", true, false, true, PENALTY_HYPHEN, -1 }, + // simple hyphen-minus: same penalties like automatic or soft hyphens + { "-", false, true, true, -1, PENALTY_HYPHEN }, + // (unconditional) hyphen (U+2010): handled exactly like minus-hyphen. + { "\xe2\x80\x90", false, true, true, -1, PENALTY_HYPHEN }, + // em dash (U+2014): breaks on both sides are allowed (but see below). + { "\xe2\x80\x94", false, true, false, + PENALTY_EM_DASH_LEFT, PENALTY_EM_DASH_RIGHT } +}; + +// Standard values are defined here. The values are already multiplied +// with 100. +// +// Some examples (details are described in doc/dw-line-breaking.doc): +// +// 0 = Perfect line; as penalty used for normal spaces. + +// 1 (100 here) = A justified line with spaces having 150% or 67% of +// the ideal space width has this as badness. +// +// 8 (800 here) = A justified line with spaces twice as wide as +// ideally has this as badness. +// +// The second value is used when the line before ends with a hyphen, +// dash etc. + +int Textblock::penalties[PENALTY_NUM][2] = { + // Penalties for all hyphens. + { 100, 800 }, + // Penalties for a break point *left* of an em-dash: rather large, + // so that a break on the *right* side is preferred. + { 800, 800 }, + // Penalties for a break point *right* of an em-dash: like hyphens. + { 100, 800 } +}; + +/** + * The character which is used to draw a hyphen at the end of a line, + * either caused by automatic hyphenation, or by soft hyphens. + * + * Initially, soft hyphens were used, but they are not drawn on some + * platforms. Also, unconditional hyphens (U+2010) are not available + * in many fonts; so, a simple hyphen-minus is used. + */ +const char *Textblock::hyphenDrawChar = "-"; + +void Textblock::setPenaltyHyphen (int penaltyHyphen) +{ + penalties[PENALTY_HYPHEN][0] = penaltyHyphen; +} + +void Textblock::setPenaltyHyphen2 (int penaltyHyphen2) +{ + penalties[PENALTY_HYPHEN][1] = penaltyHyphen2; +} + +void Textblock::setPenaltyEmDashLeft (int penaltyLeftEmDash) +{ + penalties[PENALTY_EM_DASH_LEFT][0] = penaltyLeftEmDash; + penalties[PENALTY_EM_DASH_LEFT][1] = penaltyLeftEmDash; +} + +void Textblock::setPenaltyEmDashRight (int penaltyRightEmDash) +{ + penalties[PENALTY_EM_DASH_RIGHT][0] = penaltyRightEmDash; +} + +void Textblock::setPenaltyEmDashRight2 (int penaltyRightEmDash2) +{ + penalties[PENALTY_EM_DASH_RIGHT][1] = penaltyRightEmDash2; +} + Textblock::Textblock (bool limitTextWidth) { registerName ("dw::Textblock", &CLASS_ID); @@ -313,7 +386,8 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) } // Minimum: between two *possible* breaks (or at the end). - if (word->badnessAndPenalty.lineCanBeBroken () || atLastWord) { + // TODO: Explain why index 1 is used in lineCanBeBroken(). + if (word->badnessAndPenalty.lineCanBeBroken (1) || atLastWord) { parMin += wordExtremes.minWidth + word->hyphenWidth; extremes->minWidth = misc::max (extremes->minWidth, parMin); parMin = 0; @@ -323,7 +397,9 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) parMin += wordExtremes.minWidth + word->origSpace; // Maximum: between two *necessary* breaks (or at the end). - if (word->badnessAndPenalty.lineMustBeBroken () || atLastWord) { + // TODO: lineMustBeBroken should be independent of the + // penalty index? + if (word->badnessAndPenalty.lineMustBeBroken (1) || atLastWord) { parMax += wordExtremes.maxWidth + word->hyphenWidth; extremes->maxWidth = misc::max (extremes->maxWidth, parMax); parMax = 0; @@ -1010,6 +1086,7 @@ void Textblock::drawText(core::View *view, core::style::Style *style, break; } } + view->drawText(style->font, style->color, shading, x, y, str ? str : text + start, str ? strlen(str) : len); if (str) @@ -1031,7 +1108,7 @@ void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2, { core::style::Style *style = words->getRef(wordIndex1)->style; bool drawHyphen = wordIndex2 == line->lastWord - && words->getRef(wordIndex2)->hyphenWidth > 0; + && (words->getRef(wordIndex2)->flags & Word::DIV_CHAR_AT_EOL); if (style->hasBackground ()) { int w = 0; @@ -1055,8 +1132,8 @@ void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2, l += strlen (w->content.text); totalWidth += w->size.width; } - - char text[l + (drawHyphen ? 2 : 0) + 1]; + + char text[l + (drawHyphen ? strlen (hyphenDrawChar) : 0) + 1]; int p = 0; for (int i = wordIndex1; i <= wordIndex2; i++) { const char * t = words->getRef(i)->content.text; @@ -1065,8 +1142,8 @@ void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2, } if(drawHyphen) { - text[p++] = 0xc2; - text[p++] = 0xad; + for (int i = 0; hyphenDrawChar[i]; i++) + text[p++] = hyphenDrawChar[i]; text[p++] = 0; } @@ -1249,7 +1326,8 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) } else { int wordIndex2 = wordIndex; while (wordIndex2 < line->lastWord && - words->getRef(wordIndex2)->hyphenWidth > 0 && + (words->getRef(wordIndex2)->flags + & Word::DRAW_AS_ONE_TEXT) && word->style == words->getRef(wordIndex2 + 1)->style) wordIndex2++; @@ -1263,6 +1341,7 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) word = words->getRef(wordIndex); } } + if (word->effSpace > 0 && wordIndex < line->lastWord && words->getRef(wordIndex + 1)->content.type != core::Content::BREAK) { @@ -1429,9 +1508,9 @@ void Textblock::fillWord (Word *word, int width, int ascent, int descent, word->origSpace = word->effSpace = word->stretchability = word->shrinkability = 0; word->hyphenWidth = 0; - word->badnessAndPenalty.setPenaltyProhibitBreak (); + word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK); word->content.space = false; - word->canBeHyphenated = canBeHyphenated; + word->flags = canBeHyphenated ? Word::CAN_BE_HYPHENATED : 0; word->style = style; word->spaceStyle = style; @@ -1548,79 +1627,195 @@ void Textblock::calcTextSize (const char *text, size_t len, } /** - * Add a word to the page structure. If it contains soft hyphens, it is - * divided. + * Add a word to the page structure. If it contains dividing + * characters (hard or soft hyphens, em-dashes, etc.), it is divided. */ void Textblock::addText (const char *text, size_t len, core::style::Style *style) { PRINTF ("[%p] ADD_TEXT (%d characters)\n", this, (int)len); - // Count hyphens. - int numHyphens = 0; - for (int i = 0; i < (int)len - 1; i++) - // (0xc2, 0xad) is the UTF-8 representation of a soft hyphen (Unicode - // 0xc2). - if((unsigned char)text[i] == 0xc2 && (unsigned char)text[i + 1] == 0xad) - numHyphens++; - - if (numHyphens == 0) { - // Simple (and common) case: no soft hyphens. May still be hyphenated - // automatically. + // Count dividing characters. + int numParts = 1; + + for (int i = 0; i < (int)len; + i < (int)len && (i = layout->nextGlyph (text, i))) { + int foundDiv = -1; + for (int j = 0; foundDiv == -1 && j < NUM_DIV_CHARS; j++) { + int lDiv = strlen (divChars[j].s); + if (i <= (int)len - lDiv) { + if (memcmp (text + i, divChars[j].s, lDiv * sizeof (char)) == 0) + foundDiv = j; + } + } + + if (foundDiv != -1) { + if (divChars[foundDiv].penaltyIndexLeft != -1) + numParts ++; + if (divChars[foundDiv].penaltyIndexRight != -1) + numParts ++; + } + } + + if (numParts == 1) { + // Simple (and common) case: no dividing characters. May still + // be hyphenated automatically. core::Requisition size; calcTextSize (text, len, style, &size); addText0 (text, len, true, style, &size); } else { - PRINTF("HYPHENATION: '"); + PRINTF ("HYPHENATION: '"); for (size_t i = 0; i < len; i++) PUTCHAR(text[i]); - PRINTF("', with %d hyphen(s)\n", numHyphens); + PRINTF ("', with %d parts\n", numParts); // Store hyphen positions. - int n = 0, hyphenPos[numHyphens], breakPos[numHyphens]; - for (size_t i = 0; i < len - 1; i++) - if((unsigned char)text[i] == 0xc2 && - (unsigned char)text[i + 1] == 0xad) { - hyphenPos[n] = i; - breakPos[n] = i - 2 * n; - n++; + int n = 0, totalLenCharRemoved = 0; + int partPenaltyIndex[numParts - 1]; + int partStart[numParts], partEnd[numParts]; + bool charRemoved[numParts - 1], canBeHyphenated[numParts + 1]; + bool permDivChar[numParts - 1], unbreakableForMinWidth[numParts - 1]; + canBeHyphenated[0] = canBeHyphenated[numParts] = true; + partStart[0] = 0; + partEnd[numParts - 1] = len; + + for (int i = 0; i < (int)len; + i < (int)len && (i = layout->nextGlyph (text, i))) { + int foundDiv = -1; + for (int j = 0; foundDiv == -1 && j < NUM_DIV_CHARS; j++) { + int lDiv = strlen (divChars[j].s); + if (i <= (int)len - lDiv) { + if (memcmp (text + i, divChars[j].s, lDiv * sizeof (char)) == 0) + foundDiv = j; + } + } + + if (foundDiv != -1) { + int lDiv = strlen (divChars[foundDiv].s); + + if (divChars[foundDiv].charRemoved) { + assert (divChars[foundDiv].penaltyIndexLeft != -1); + assert (divChars[foundDiv].penaltyIndexRight == -1); + + partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexLeft; + charRemoved[n] = true; + permDivChar[n] = false; + unbreakableForMinWidth[n] = + divChars[foundDiv].unbreakableForMinWidth; + canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated; + partEnd[n] = i; + partStart[n + 1] = i + lDiv; + n++; + totalLenCharRemoved += lDiv; + } else { + assert (divChars[foundDiv].penaltyIndexLeft != -1 || + divChars[foundDiv].penaltyIndexRight != -1); + + if (divChars[foundDiv].penaltyIndexLeft != -1) { + partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexLeft; + charRemoved[n] = false; + permDivChar[n] = false; + unbreakableForMinWidth[n] = + divChars[foundDiv].unbreakableForMinWidth; + canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated; + partEnd[n] = i; + partStart[n + 1] = i; + n++; + } + + if (divChars[foundDiv].penaltyIndexRight != -1) { + partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexRight; + charRemoved[n] = false; + permDivChar[n] = true; + unbreakableForMinWidth[n] = + divChars[foundDiv].unbreakableForMinWidth; + canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated; + partEnd[n] = i + lDiv; + partStart[n + 1] = i + lDiv; + n++; + } + } } + } - // Get text without hyphens. (There are numHyphens + 1 parts in the word, - // and 2 * numHyphens bytes less, 2 for each hyphen, are needed.) - char textWithoutHyphens[len - 2 * numHyphens]; - int start = 0; // related to "text" - for (int i = 0; i < numHyphens + 1; i++) { - int end = (i == numHyphens) ? len : hyphenPos[i]; - memmove (textWithoutHyphens + start - 2 * i, text + start, - end - start); - start = end + 2; + // Get text without removed characters, e. g. hyphens. + const char *textWithoutHyphens; + char textWithoutHyphensBuf[len - totalLenCharRemoved]; + int *breakPosWithoutHyphens, breakPosWithoutHyphensBuf[numParts - 1]; + + if (totalLenCharRemoved == 0) { + // No removed characters: take original arrays. + textWithoutHyphens = text; + // Ends are also break positions, except the last end, which + // is superfluous, but does not harm (since arrays in C/C++ + // does not have an implicit length). + breakPosWithoutHyphens = partEnd; + } else { + // Copy into special buffers. + textWithoutHyphens = textWithoutHyphensBuf; + breakPosWithoutHyphens = breakPosWithoutHyphensBuf; + + int n = 0; + for (int i = 0; i < numParts; i++) { + memmove (textWithoutHyphensBuf + n, text + partStart[i], + partEnd[i] - partStart[i]); + n += partEnd[i] - partStart[i]; + if (i < numParts - 1) + breakPosWithoutHyphensBuf[i] = n; + } } PRINTF("H... without hyphens: '"); - for (size_t i = 0; i < len - 2 * numHyphens; i++) + for (size_t i = 0; i < len - totalLenCharRemoved; i++) PUTCHAR(textWithoutHyphens[i]); PRINTF("'\n"); - core::Requisition wordSize[numHyphens + 1]; - calcTextSizes (textWithoutHyphens, len - 2 * numHyphens, style, - numHyphens, breakPos, wordSize); + core::Requisition wordSize[numParts]; + calcTextSizes (textWithoutHyphens, len - totalLenCharRemoved, style, + numParts - 1, breakPosWithoutHyphens, wordSize); // Finished! - for (int i = 0; i < numHyphens + 1; i++) { - int start = (i == 0) ? 0 : hyphenPos[i - 1] + 2; - int end = (i == numHyphens) ? len : hyphenPos[i]; - // Do not anymore hyphen automatically. - addText0 (text + start, end - start, false, style, &wordSize[i]); + for (int i = 0; i < numParts; i++) { + addText0 (text + partStart[i], partEnd[i] - partStart[i], + // If this parts adjoins at least one division + // characters, for which canBeHyphenated is set to + // false (this is the case for soft hyphens), do + // not hyphenate. + canBeHyphenated[i] && canBeHyphenated[i + 1], + style, &wordSize[i]); PRINTF("H... [%d] '", i); - for (int j = start; j < end; j++) + for (int j = partStart[i]; j < partEnd[i]; j++) PUTCHAR(text[j]); PRINTF("' added\n"); - if(i < numHyphens) { - addHyphen (); - PRINTF("H... yphen added\n"); + if(i < numParts - 1) { + Word *word = words->getLastRef(); + + word->badnessAndPenalty + .setPenalties (penalties[partPenaltyIndex[i]][0], + penalties[partPenaltyIndex[i]][1]); + + if (charRemoved[i]) { + // Currently, only unconditional hyphens (UTF-8: + // "\xe2\x80\x90") can be used. See also drawWord, last + // section "if (drawHyphen)". + // Could be extended by adding respective members to + // DivChar and Word. + word->hyphenWidth = + layout->textWidth (word->style->font, hyphenDrawChar, + strlen (hyphenDrawChar)); + word->flags |= Word::DIV_CHAR_AT_EOL; + } + + if (permDivChar[i]) + word->flags |= Word::PERM_DIV_CHAR; + if (unbreakableForMinWidth[i]) + word->flags |= Word::UNBREAKABLE_FOR_MIN_WIDTH; + + word->flags |= Word::DRAW_AS_ONE_TEXT; + + accumulateWordData (words->size() - 1); } } } @@ -1641,7 +1836,8 @@ void Textblock::calcTextSizes (const char *text, size_t textLen, PUTCHAR(text[i + lastStart]); PRINTF("' -> %d\n", wordSize[numBreaks].width); - // The rest is more complicated. TODO Documentation. + // The rest is more complicated. See dw-line-breaking, section + // "Hyphens". for (int i = numBreaks - 1; i >= 0; i--) { int start = (i == 0) ? 0 : breakPos[i - 1]; calcTextSize (text + start, textLen - start, style, &wordSize[i]); @@ -1664,6 +1860,11 @@ void Textblock::calcTextSizes (const char *text, size_t textLen, void Textblock::addText0 (const char *text, size_t len, bool canBeHyphenated, core::style::Style *style, core::Requisition *size) { + //printf("[%p] addText0 ('", this); + //for (size_t i = 0; i < len; i++) + // putchar(text[i]); + //printf("', %s, ...)\n", canBeHyphenated ? "true" : "false"); + Word *word = addWord (size->width, size->ascent, size->descent, canBeHyphenated, style); word->content.type = core::Content::TEXT; @@ -1826,7 +2027,9 @@ void Textblock::fillSpace (Word *word, core::style::Style *style) */ void Textblock::setBreakOption (Word *word, core::style::Style *style) { - if (!word->badnessAndPenalty.lineMustBeBroken()) { + // TODO: lineMustBeBroken should be independent of the penalty + // index? Otherwise, examine the last line. + if (!word->badnessAndPenalty.lineMustBeBroken(0)) { switch (style->whiteSpace) { case core::style::WHITE_SPACE_NORMAL: case core::style::WHITE_SPACE_PRE_LINE: @@ -1836,26 +2039,12 @@ void Textblock::setBreakOption (Word *word, core::style::Style *style) case core::style::WHITE_SPACE_PRE: case core::style::WHITE_SPACE_NOWRAP: - word->badnessAndPenalty.setPenaltyProhibitBreak (); + word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK); break; } } } -void Textblock::addHyphen () -{ - int wordIndex = words->size () - 1; - - if (wordIndex >= 0) { - Word *word = words->getRef(wordIndex); - - word->badnessAndPenalty.setPenalty (HYPHEN_BREAK); - // TODO Optimize? Like spaces? - word->hyphenWidth = layout->textWidth (word->style->font, "\xc2\xad", 2); - - accumulateWordData (wordIndex); - } -} /** * Cause a paragraph break @@ -1930,7 +2119,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) word = addWord (0, 0, 0, false, style); word->content.type = core::Content::BREAK; - word->badnessAndPenalty.setPenaltyForceBreak (); + word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK); word->content.breakSpace = space; wordWrap (words->size () - 1, false); } @@ -1953,7 +2142,7 @@ void Textblock::addLinebreak (core::style::Style *style) word = addWord (0, 0, 0, false, style); word->content.type = core::Content::BREAK; - word->badnessAndPenalty.setPenaltyForceBreak (); + word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK); word->content.breakSpace = 0; wordWrap (words->size () - 1, false); } @@ -2058,14 +2247,13 @@ void Textblock::changeLinkColor (int link, int newColor) styleAttrs = *old_style; styleAttrs.color = core::style::Color::create (layout, newColor); - word->style = core::style::Style::create (layout, &styleAttrs); + word->style = core::style::Style::create (&styleAttrs); old_style->unref(); old_style = word->spaceStyle; styleAttrs = *old_style; styleAttrs.color = core::style::Color::create (layout, newColor); - word->spaceStyle = - core::style::Style::create(layout, &styleAttrs); + word->spaceStyle = core::style::Style::create(&styleAttrs); old_style->unref(); break; } @@ -2076,8 +2264,7 @@ void Textblock::changeLinkColor (int link, int newColor) newColor); styleAttrs.setBorderColor( core::style::Color::create (layout, newColor)); - widget->setStyle( - core::style::Style::create (layout, &styleAttrs)); + widget->setStyle(core::style::Style::create (&styleAttrs)); break; } default: diff --git a/dw/textblock.hh b/dw/textblock.hh index 3d1f676c..c7d53eeb 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -154,14 +154,19 @@ private: * badness is not well defined, so fiddling with the penalties is a * bit difficult. */ + + enum { + PENALTY_FORCE_BREAK = INT_MIN, + PENALTY_PROHIBIT_BREAK = INT_MAX + }; + class BadnessAndPenalty { private: enum { NOT_STRETCHABLE, QUITE_LOOSE, BADNESS_VALUE, TOO_TIGHT } badnessState; - enum { FORCE_BREAK, PROHIBIT_BREAK, PENALTY_VALUE } penaltyState; int ratio; // ratio is only defined when badness is defined - int badness, penalty; + int badness, penalty[2]; // For debugging: define DEBUG for more informations in print(). #ifdef DEBUG @@ -193,39 +198,45 @@ private: // etc. works. }; + void setSinglePenalty (int index, int penalty); int badnessValue (int infLevel); - int penaltyValue (int infLevel); + int penaltyValue (int index, int infLevel); public: void calcBadness (int totalWidth, int idealWidth, int totalStretchability, int totalShrinkability); - void setPenalty (int penalty); - void setPenaltyProhibitBreak (); - void setPenaltyForceBreak (); + inline void setPenalty (int penalty) { setPenalties (penalty, penalty); } + void setPenalties (int penalty1, int penalty2); bool lineLoose (); bool lineTight (); bool lineTooTight (); - bool lineMustBeBroken (); - bool lineCanBeBroken (); - int compareTo (BadnessAndPenalty *other); + bool lineMustBeBroken (int penaltyIndex); + bool lineCanBeBroken (int penaltyIndex); + int compareTo (int penaltyIndex, BadnessAndPenalty *other); void print (); }; + enum { PENALTY_HYPHEN, PENALTY_EM_DASH_LEFT, PENALTY_EM_DASH_RIGHT, + PENALTY_NUM }; + enum { NUM_DIV_CHARS = 4 }; + + typedef struct + { + const char *s; + bool charRemoved, canBeHyphenated, unbreakableForMinWidth; + int penaltyIndexLeft, penaltyIndexRight; + } DivChar; + + static DivChar divChars[NUM_DIV_CHARS]; + + static const char *hyphenDrawChar; + Textblock *containingBlock; OutOfFlowMgr *outOfFlowMgr; protected: - enum { - /** - * The penalty for hyphens, multiplied by 100. So, 100 means - * 1.0. See dw::Textblock::BadnessAndPenalty::setPenalty for - * more details. - */ - HYPHEN_BREAK = 100 - }; - struct Line { int firstWord; /* first word's index in word vector */ @@ -273,6 +284,26 @@ protected: struct Word { + enum { + /** Can be hyphenated automatically. (Cleared after + * hyphenation.) */ + CAN_BE_HYPHENATED = 1 << 0, + /** Must be drawn with a hyphen, when at the end of the line. */ + DIV_CHAR_AT_EOL = 1 << 1, + /** Is or ends with a "division character", which is part of + * the word. */ + PERM_DIV_CHAR = 1 << 2, + /** This word must be drawn, together with the following + * word(s), by only one call of View::drawText(), to get + * kerning, ligatures etc. right. The last of the words drawn + * as one text does *not* have this flag set. */ + DRAW_AS_ONE_TEXT = 1 << 3, + /* When calculating the minimal width (as part of extremes), + * do not consider this word as breakable. This flag is + * ignored when the line is actually broken. */ + UNBREAKABLE_FOR_MIN_WIDTH = 1 << 4, + }; + /* TODO: perhaps add a xLeft? */ core::Requisition size; /* Space after the word, only if it's not a break: */ @@ -286,8 +317,9 @@ protected: * this is the last word of the line, and * "hyphenWidth > 0" is also used to decide * whether to draw a hyphen. */ + short flags; + short penaltyIndex; core::Content content; - bool canBeHyphenated; // accumulated values, relative to the beginning of the line int totalWidth; /* The sum of all word widths; plus all @@ -370,6 +402,14 @@ protected: bool mustQueueResize; + /** + * The penalties for hyphens and other, multiplied by 100. So, 100 + * means 1.0. INT_MAX and INT_MIN are also allowed. See + * dw::Textblock::BadnessAndPenalty::setPenalty for more + * details. Set from preferences. + */ + static int penalties[PENALTY_NUM][2]; + bool limitTextWidth; /* from preferences */ int redrawY; @@ -561,6 +601,16 @@ protected: { return lineYOffsetCanvas (lines->getRef (lineIndex)); } + + inline int calcPenaltyIndexForNewLine () + { + if (lines->size() == 0) + return 0; + else + return + (words->getRef(lines->getLastRef()->lastWord)->flags & + (Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0; + } Textblock *getTextblockForLine (Line *line); Textblock *getTextblockForLine (int lineNo); @@ -613,6 +663,12 @@ protected: public: static int CLASS_ID; + static void setPenaltyHyphen (int penaltyHyphen); + static void setPenaltyHyphen2 (int penaltyHyphen2); + static void setPenaltyEmDashLeft (int penaltyLeftEmDash); + static void setPenaltyEmDashRight (int penaltyRightEmDash); + static void setPenaltyEmDashRight2 (int penaltyRightEmDash2); + Textblock(bool limitTextWidth); ~Textblock(); @@ -640,7 +696,6 @@ public: setBreakOption (words->getRef(wordIndex), style); } - void addHyphen(); void addParbreak (int space, core::style::Style *style); void addLinebreak (core::style::Style *style); diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index f97bd9b5..67798fd6 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -31,22 +31,14 @@ int Textblock::BadnessAndPenalty::badnessValue (int infLevel) return 0; } -int Textblock::BadnessAndPenalty::penaltyValue (int infLevel) +int Textblock::BadnessAndPenalty::penaltyValue (int index, int infLevel) { - switch (penaltyState) { - case FORCE_BREAK: + if (penalty[index] == INT_MIN) return infLevel == INF_PENALTIES ? -1 : 0; - - case PROHIBIT_BREAK: + else if (penalty[index] == INT_MAX) return infLevel == INF_PENALTIES ? 1 : 0; - - case PENALTY_VALUE: - return infLevel == INF_VALUE ? penalty : 0; - } - - // compiler happiness - lout::misc::assertNotReached (); - return 0; + else + return infLevel == INF_VALUE ? penalty[index] : 0; } void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth, @@ -98,6 +90,9 @@ void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth, * to deal with fractional numbers, without having to use floating * point numbers. So, to set a penalty to 0.5, pass 50. * + * INT_MAX and INT_MIN (representing inf and -inf, respectively) are + * also allowed. + * * The definition of penalties depends on the definition of badness, * which adheres to the description in \ref dw-line-breaking, section * "Criteria for Line-Breaking". The exact calculation may vary, but @@ -105,27 +100,28 @@ void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth, * fitting line has a badness of 0. (ii) A line, where all spaces * are extended by exactly the stretchability, as well as a line, where * all spaces are reduced by the shrinkability, have a badness of 1. + * + * (TODO plural: penalties, not penalty. Correct above comment) */ -void Textblock::BadnessAndPenalty::setPenalty (int penalty) -{ - // This factor consists of: (i) 100^3, since in calcBadness(), the - // ratio is multiplied with 100 (again, to use integer numbers for - // fractional numbers), and the badness (which has to be compared - // to the penalty!) is the third power or it; (ii) the denominator - // 100, of course, since 100 times the penalty is passed to this - // method. - this->penalty = penalty * (100 * 100 * 100 / 100); - penaltyState = PENALTY_VALUE; -} - -void Textblock::BadnessAndPenalty::setPenaltyProhibitBreak () +void Textblock::BadnessAndPenalty::setPenalties (int penalty1, int penalty2) { - penaltyState = PROHIBIT_BREAK; + // TODO Check here some cases, e.g. both or no penalty INT_MIN. + setSinglePenalty(0, penalty1); + setSinglePenalty(1, penalty2); } -void Textblock::BadnessAndPenalty::setPenaltyForceBreak () +void Textblock::BadnessAndPenalty::setSinglePenalty (int index, int penalty) { - penaltyState = FORCE_BREAK; + if (penalty == INT_MAX || penalty == INT_MIN) + this->penalty[index] = penalty; + else + // This factor consists of: (i) 100^3, since in calcBadness(), the + // ratio is multiplied with 100 (again, to use integer numbers for + // fractional numbers), and the badness (which has to be compared + // to the penalty!) is the third power or it; (ii) the denominator + // 100, of course, since 100 times the penalty is passed to this + // method. + this->penalty[index] = penalty * (100 * 100 * 100 / 100); } bool Textblock::BadnessAndPenalty::lineLoose () @@ -147,21 +143,23 @@ bool Textblock::BadnessAndPenalty::lineTooTight () } -bool Textblock::BadnessAndPenalty::lineMustBeBroken () +bool Textblock::BadnessAndPenalty::lineMustBeBroken (int penaltyIndex) { - return penaltyState == FORCE_BREAK; + return penalty[penaltyIndex] == PENALTY_FORCE_BREAK; } -bool Textblock::BadnessAndPenalty::lineCanBeBroken () +bool Textblock::BadnessAndPenalty::lineCanBeBroken (int penaltyIndex) { - return penaltyState != PROHIBIT_BREAK; + return penalty[penaltyIndex] != PENALTY_PROHIBIT_BREAK; } -int Textblock::BadnessAndPenalty::compareTo (BadnessAndPenalty *other) +int Textblock::BadnessAndPenalty::compareTo (int penaltyIndex, + BadnessAndPenalty *other) { for (int l = INF_MAX; l >= 0; l--) { - int thisValue = badnessValue (l) + penaltyValue (l); - int otherValue = other->badnessValue (l) + other->penaltyValue (l); + int thisValue = badnessValue (l) + penaltyValue (penaltyIndex, l); + int otherValue = + other->badnessValue (l) + other->penaltyValue (penaltyIndex, l); if (thisValue != otherValue) return thisValue - otherValue; @@ -197,19 +195,19 @@ void Textblock::BadnessAndPenalty::print () printf (" <no debug> + "); #endif - switch (penaltyState) { - case FORCE_BREAK: - printf ("-inf"); - break; - - case PROHIBIT_BREAK: - printf ("inf"); - break; + printf ("("); + for (int i = 0; i < 2; i++) { + if (penalty[i] == INT_MIN) + printf ("-inf"); + else if (penalty[i] == INT_MAX) + printf ("inf"); + else + printf ("%d", penalty[i]); - case PENALTY_VALUE: - printf ("%d", penalty); - break; + if (i == 0) + printf (", "); } + printf (")"); } void Textblock::printWordShort (Word *word) @@ -237,11 +235,14 @@ void Textblock::printWordShort (Word *word) printf ("<?>"); break; } + + printf (" (flags = %d)", word->flags); } void Textblock::printWord (Word *word) { printWordShort (word); + printf (" [%d / %d + %d - %d => %d + %d - %d] => ", word->size.width, word->origSpace, word->stretchability, word->shrinkability, word->totalWidth, word->totalStretchability, @@ -317,6 +318,11 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, Word *lastWordOfLine = words->getRef(lastWord); // Word::totalWidth includes the hyphen (which is what we want here). int lineWidth = lastWordOfLine->totalWidth; + // "lineWidth" is relative to leftOffset, so we may have to add + // "line1OffsetEff" (remember: this is, for list items, negative). + if (lines->size () == 0) + lineWidth += line1OffsetEff; + int maxOfMinWidth, sumOfMaxWidth; accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth, &sumOfMaxWidth); @@ -375,7 +381,9 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->maxParMin = misc::max (maxOfMinWidth, prevLine->maxParMin); Word *lastWordOfPrevLine = words->getRef (prevLine->lastWord); - if (lastWordOfPrevLine->badnessAndPenalty.lineMustBeBroken ()) + // TODO: lineMustBeBroken should be independent of the penalty + // index? Otherwise, examine the last line. + if (lastWordOfPrevLine->badnessAndPenalty.lineMustBeBroken (0)) // This line starts a new paragraph. line->parMax = sumOfMaxWidth; else @@ -388,7 +396,9 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, // "maxParMax" is only set, when this line is the last line of the // paragraph. Word *lastWordOfThisLine = words->getRef (line->lastWord); - if (lastWordOfThisLine->badnessAndPenalty.lineMustBeBroken ()) + // TODO: lineMustBeBroken should be independent of the penalty + // index? Otherwise, examine the last line. + if (lastWordOfThisLine->badnessAndPenalty.lineMustBeBroken (0)) // Paragraph ends here. line->maxParMax = misc::max (lastMaxParMax, @@ -439,7 +449,8 @@ void Textblock::accumulateWordExtremes (int firstWord, int lastWord, // Minimum: between two *possible* breaks (or at the end). // TODO This is redundant to getExtremesImpl(). - if (word->badnessAndPenalty.lineCanBeBroken () || atLastWord) { + // TODO: Again, index 1 is used for lineCanBeBroken(). See getExtremes(). + if (word->badnessAndPenalty.lineCanBeBroken (1) || atLastWord) { parMin += extremes.minWidth + word->hyphenWidth; *maxOfMinWidth = misc::max (*maxOfMinWidth, parMin); parMin = 0; @@ -497,7 +508,6 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) // even earlier. diffYToContainingBlock + top + getStyle()->boxOffsetY()); - // TODO: compare old/new values of calcAvailWidth(...); int firstIndex = lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1; @@ -505,6 +515,8 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) accumulateWordData (i); } + int penaltyIndex = calcPenaltyIndexForNewLine (); + bool newLine; do { bool tempNewLine = false; @@ -518,14 +530,16 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) tempNewLine = true; PRINTF (" NEW LINE: last word\n"); } else if (wordIndex >= firstIndex && - word->badnessAndPenalty.lineMustBeBroken ()) { + // TODO: lineMustBeBroken should be independent of + // the penalty index? + word->badnessAndPenalty.lineMustBeBroken (penaltyIndex)) { newLine = true; searchUntil = wordIndex; PRINTF (" NEW LINE: forced break\n"); } else if (wordIndex > firstIndex && word->badnessAndPenalty.lineTooTight () && words->getRef(wordIndex - 1) - ->badnessAndPenalty.lineCanBeBroken ()) { + ->badnessAndPenalty.lineCanBeBroken (penaltyIndex)) { // TODO Comment the last condition (also below where the minimum is // searched for) newLine = true; @@ -568,7 +582,8 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) if (breakPos == -1 || w->badnessAndPenalty.compareTo - (&words->getRef(breakPos)->badnessAndPenalty) <= 0) + (penaltyIndex, + &words->getRef(breakPos)->badnessAndPenalty) <= 0) // "<=" instead of "<" in the next lines tends to result in // more words per line -- theoretically. Practically, the // case "==" will never occur. @@ -592,7 +607,8 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) BadnessAndPenalty correctedBap = lastWord->badnessAndPenalty; correctedBap.setPenalty (0); if (correctedBap.compareTo - (&words->getRef(breakPos)->badnessAndPenalty) <= 0) { + (penaltyIndex, + &words->getRef(breakPos)->badnessAndPenalty) <= 0) { breakPos = searchUntil; PRINTF (" corrected: breakPos = %d\n", breakPos); } @@ -605,7 +621,7 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) PRINTF ("\n"); if (word1->badnessAndPenalty.lineTight () && - word1->canBeHyphenated && + (word1->flags & Word::CAN_BE_HYPHENATED) && word1->style->x_lang[0] && word1->content.type == core::Content::TEXT && Hyphenator::isHyphenationCandidate (word1->content.text)) @@ -614,7 +630,7 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) if (word1->badnessAndPenalty.lineLoose () && breakPos + 1 < words->size ()) { Word *word2 = words->getRef(breakPos + 1); - if (word2->canBeHyphenated && + if ((word2->flags & Word::CAN_BE_HYPHENATED) && word2->style->x_lang[0] && word2->content.type == core::Content::TEXT && Hyphenator::isHyphenationCandidate (word2->content.text)) @@ -631,6 +647,7 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) tempNewLine ? "temporally" : "permanently", firstIndex, breakPos); lineAdded = true; + penaltyIndex = calcPenaltyIndexForNewLine (); } else { // TODO hyphenateWord() should return whether something has // changed at all. So that a second run, with @@ -735,9 +752,15 @@ int Textblock::hyphenateWord (int wordIndex) // Note: there are numBreaks + 1 word parts. if (i < numBreaks) { // TODO There should be a method fillHyphen. - w->badnessAndPenalty.setPenalty (HYPHEN_BREAK); + w->badnessAndPenalty.setPenalties (penalties[PENALTY_HYPHEN][0], + penalties[PENALTY_HYPHEN][1]); + // "\xe2\x80\x90" is an unconditional hyphen. w->hyphenWidth = - layout->textWidth (origWord.style->font, "\xc2\xad", 2); + layout->textWidth (w->style->font, hyphenDrawChar, + strlen (hyphenDrawChar)); + w->flags |= (Word::DRAW_AS_ONE_TEXT | Word::DIV_CHAR_AT_EOL | + Word::UNBREAKABLE_FOR_MIN_WIDTH); + PRINTF (" [%d] + hyphen\n", wordIndex + i); } else { if (origWord.content.space) { @@ -765,9 +788,8 @@ int Textblock::hyphenateWord (int wordIndex) origWord.spaceStyle->unref (); free (breakPos); - } else { - words->getRef(wordIndex)->canBeHyphenated = false; - } + } else + words->getRef(wordIndex)->flags &= ~Word::CAN_BE_HYPHENATED; return numBreaks; } @@ -421,6 +421,7 @@ class SelectionResource: public Resource { public: virtual void addItem (const char *str, bool enabled, bool selected) = 0; + virtual void setItem (int index, bool selected) = 0; virtual void pushGroup (const char *name, bool enabled) = 0; virtual void popGroup () = 0; @@ -480,7 +481,8 @@ public: class EntryResource: public TextResource { public: - enum { UNLIMITED_MAX_LENGTH = -1 }; + enum { UNLIMITED_SIZE = -1 }; + virtual void setMaxLength (int maxlen) = 0; }; class MultiLineTextResource: public TextResource @@ -540,7 +542,7 @@ public: virtual ListResource *createListResource (ListResource::SelectionMode selectionMode, int rows) = 0; virtual OptionMenuResource *createOptionMenuResource () = 0; - virtual EntryResource *createEntryResource (int maxLength, bool password, + virtual EntryResource *createEntryResource (int size, bool password, const char *label) = 0; virtual MultiLineTextResource *createMultiLineTextResource (int cols, int rows) = 0; diff --git a/dw/widget.cc b/dw/widget.cc index 4b50f05a..7d9f0bbc 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -111,7 +111,8 @@ void Widget::setParent (Widget *parent) notifySetParent(); //DBG_OBJ_ASSOC (widget, parent); - //printf ("%p becomes a child of %p\n", this, parent); + //printf ("The %s %p becomes a child of the %s %p\n", + // getClassName(), this, parent->getClassName(), parent); } void Widget::queueDrawArea (int x, int y, int width, int height) diff --git a/dw/widget.hh b/dw/widget.hh index e18344c7..b50269cf 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -203,7 +203,7 @@ protected: inline void setCursor (style::Cursor cursor) { layout->setCursor (cursor); } - +#if 0 inline bool selectionButtonPress (Iterator *it, int charPos, int linkNo, EventButton *event, bool withinContent) { return layout->selectionState.buttonPress (it, charPos, linkNo, event); } @@ -215,7 +215,7 @@ protected: inline bool selectionButtonMotion (Iterator *it, int charPos, int linkNo, EventMotion *event, bool withinContent) { return layout->selectionState.buttonMotion (it, charPos, linkNo, event); } - +#endif inline bool selectionHandleEvent (SelectionState::EventType eventType, Iterator *it, int charPos, int linkNo, MousePositionEvent *event) @@ -302,7 +302,11 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server) /* Let's be kind and make the HTTP query string for the dpi */ char *proxy_connect = a_Http_make_connect_str(web->url); Dstr *http_query = a_Http_make_query_str(web->url, web->requester,FALSE); - /* BUG: embedded NULLs in query data will truncate message */ + + if ((uint_t) http_query->len > strlen(http_query->str)) { + /* Can't handle NULLs embedded in query data */ + MSG_ERR("HTTPS query truncated!\n"); + } /* BUG: WORKAROUND: request to only check the root URL's certificate. * This avoids the dialog bombing that stems from loading multiple diff --git a/src/decode.c b/src/decode.c index 7ea3dc25..c5752e62 100644 --- a/src/decode.c +++ b/src/decode.c @@ -242,24 +242,6 @@ Decode *a_Decode_content_init(const char *format) } /* - * Legal names for the ASCII character set - */ -static int Decode_is_ascii(const char *str) -{ - return (!(dStrAsciiCasecmp(str, "ASCII") && - dStrAsciiCasecmp(str, "US-ASCII") && - dStrAsciiCasecmp(str, "us") && - dStrAsciiCasecmp(str, "IBM367") && - dStrAsciiCasecmp(str, "cp367") && - dStrAsciiCasecmp(str, "csASCII") && - dStrAsciiCasecmp(str, "ANSI_X3.4-1968") && - dStrAsciiCasecmp(str, "iso-ir-6") && - dStrAsciiCasecmp(str, "ANSI_X3.4-1986") && - dStrAsciiCasecmp(str, "ISO_646.irv:1991") && - dStrAsciiCasecmp(str, "ISO646-US"))); -} - -/* * Initialize decoder to translate from any character set known to iconv() * to UTF-8. * @@ -272,8 +254,7 @@ Decode *a_Decode_charset_init(const char *format) if (format && strlen(format) && - dStrAsciiCasecmp(format,"UTF-8") && - !Decode_is_ascii(format)) { + dStrAsciiCasecmp(format,"UTF-8")) { iconv_t ic = iconv_open("UTF-8", format); if (ic != (iconv_t) -1) { diff --git a/src/dialog.cc b/src/dialog.cc index 1b8fd2db..6a8fd071 100644 --- a/src/dialog.cc +++ b/src/dialog.cc @@ -13,6 +13,7 @@ #include <math.h> // for rint() +#include <FL/fl_ask.H> #include <FL/Fl_Window.H> #include <FL/Fl_File_Chooser.H> #include <FL/Fl_Return_Button.H> diff --git a/src/dillo.cc b/src/dillo.cc index 173ac036..9bab589c 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -50,6 +50,7 @@ #include "auth.h" #include "dw/fltkcore.hh" +#include "dw/textblock.hh" /* * Command line options structure @@ -359,6 +360,9 @@ int main(int argc, char **argv) a_Cookies_init(); a_Auth_init(); + dw::Textblock::setPenaltyHyphen (prefs.penalty_hyphen); + dw::Textblock::setPenaltyHyphen2 (prefs.penalty_hyphen_2); + /* command line options override preferences */ if (options_got & DILLO_CLI_FULLWINDOW) prefs.fullwindow_start = TRUE; diff --git a/src/domain.c b/src/domain.c index af8c8075..90d6b414 100644 --- a/src/domain.c +++ b/src/domain.c @@ -132,17 +132,17 @@ bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest) if (a_Url_same_organization(source, dest)) return TRUE; + ret = default_deny ? FALSE : TRUE; + for (i = 0; i < num_exceptions; i++) { - if(Domain_match(source_host, exceptions[i].origin) && - Domain_match(dest_host, exceptions[i].destination)) { + if (Domain_match(source_host, exceptions[i].origin) && + Domain_match(dest_host, exceptions[i].destination)) { ret = default_deny; MSG("Domain: Matched rule from %s to %s.\n", exceptions[i].origin, exceptions[i].destination); break; } } - if (i == num_exceptions) - ret = default_deny ? FALSE : TRUE; MSG("Domain: %s from %s to %s.\n", (ret == TRUE ? "permitted" : "DENIED"), source_host, dest_host); diff --git a/src/form.cc b/src/form.cc index 11f27b47..05042569 100644 --- a/src/form.cc +++ b/src/form.cc @@ -177,6 +177,7 @@ public: void addOption (char *value, bool selected, bool enabled); void ensureSelection (); void addOptionsTo (SelectionResource *res); + void reset (SelectionResource *res); void appendValuesTo (Dlist *values, SelectionResource *res); }; @@ -360,7 +361,7 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) a_Url_free(action); } -void Html_tag_close_form(DilloHtml *html, int TagIdx) +void Html_tag_close_form(DilloHtml *html) { // DilloHtmlForm *form; // int i; @@ -547,10 +548,11 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) if (a_Html_get_attr(html, tag, tagsize, "readonly")) ((EntryResource *) resource)->setEditable(false); -// /* Maximum length of the text in the entry */ -// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "maxlength"))) -// gtk_entry_set_max_length(GTK_ENTRY(widget), -// strtol(attrbuf, NULL, 10)); + /* Maximum length of the text in the entry */ + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "maxlength"))) { + int maxlen = strtol(attrbuf, NULL, 10); + ((EntryResource *) resource)->setMaxLength(maxlen); + } } if (prefs.show_tooltip && (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) { @@ -680,7 +682,7 @@ void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize) * Close textarea * (TEXTAREA is parsed in VERBATIM mode, and entities are handled here) */ -void Html_tag_close_textarea(DilloHtml *html, int TagIdx) +void Html_tag_close_textarea(DilloHtml *html) { char *str; DilloHtmlInput *input; @@ -750,11 +752,12 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize) type = DILLO_HTML_INPUT_SELECT; res = factory->createOptionMenuResource (); } else { + ListResource::SelectionMode mode; + type = DILLO_HTML_INPUT_SEL_LIST; - res = factory->createListResource (multi ? - ListResource::SELECTION_MULTIPLE : - ListResource::SELECTION_EXACTLY_ONE, - rows); + mode = multi ? ListResource::SELECTION_MULTIPLE + : ListResource::SELECTION_AT_MOST_ONE; + res = factory->createListResource (mode, rows); } Embed *embed = new Embed(res); @@ -774,7 +777,7 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize) /* * ? */ -void Html_tag_close_select(DilloHtml *html, int TagIdx) +void Html_tag_close_select(DilloHtml *html) { if (html->InFlags & IN_SELECT) { if (html->InFlags & IN_OPTION) @@ -785,9 +788,10 @@ void Html_tag_close_select(DilloHtml *html, int TagIdx) DilloHtmlInput *input = Html_get_current_input(html); DilloHtmlSelect *select = input->select; - // BUG(?): should not do this for MULTI selections - select->ensureSelection (); - + if (input->type == DILLO_HTML_INPUT_SELECT) { + // option menu interface requires that something be selected */ + select->ensureSelection (); + } SelectionResource *res = (SelectionResource*)input->embed->getResource(); select->addOptionsTo (res); } @@ -868,7 +872,7 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize) html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, attrbuf); } - /* We used to have Textblock (prefs.limit_text_width) here, + /* We used to have Textblock (prefs.limit_text_width, ...) here, * but it caused 100% CPU usage. */ page = new Textblock (false); @@ -898,7 +902,7 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize) /* * Handle close <BUTTON> */ -void Html_tag_close_button(DilloHtml *html, int TagIdx) +void Html_tag_close_button(DilloHtml *html) { html->InFlags &= ~IN_BUTTON; } @@ -982,8 +986,6 @@ void DilloHtmlForm::submit(DilloHtmlInput *active_input, EventButton *event) } a_Url_free(url); } - // /* now, make the rendered area have its focus back */ - // gtk_widget_grab_focus(GTK_BIN(bw->render_main_scroll)->child); } /* @@ -1764,35 +1766,12 @@ void DilloHtmlInput::reset () } break; case DILLO_HTML_INPUT_SELECT: + case DILLO_HTML_INPUT_SEL_LIST: if (select != NULL) { - /* this is in reverse order so that, in case more than one was - * selected, we get the last one, which is consistent with handling - * of multiple selected options in the layout code. */ -// for (i = select->num_options - 1; i >= 0; i--) { -// if (select->options[i].init_val) { -// gtk_menu_item_activate(GTK_MENU_ITEM -// (select->options[i].menuitem)); -// Html_select_set_history(input); -// break; -// } -// } + SelectionResource *sr = (SelectionResource *) embed->getResource(); + select->reset(sr); } break; - case DILLO_HTML_INPUT_SEL_LIST: - if (!select) - break; -// for (i = 0; i < select->num_options; i++) { -// if (select->options[i].init_val) { -// if (select->options[i].menuitem->state == GTK_STATE_NORMAL) -// gtk_list_select_child(GTK_LIST(select->menu), -// select->options[i].menuitem); -// } else { -// if (select->options[i].menuitem->state==GTK_STATE_SELECTED) -// gtk_list_unselect_child(GTK_LIST(select->menu), -// select->options[i].menuitem); -// } -// } - break; case DILLO_HTML_INPUT_TEXTAREA: if (init_str != NULL) { MultiLineTextResource *textres = @@ -1874,6 +1853,15 @@ void DilloHtmlSelect::addOptionsTo (SelectionResource *res) } } +void DilloHtmlSelect::reset (SelectionResource *res) +{ + int size = options->size (); + for (int i = 0; i < size; i++) { + DilloHtmlOption *option = options->get (i); + res->setItem(i, option->selected); + } +} + void DilloHtmlSelect::appendValuesTo (Dlist *values, SelectionResource *res) { int size = options->size (); diff --git a/src/form.hh b/src/form.hh index cd04543f..297442e0 100644 --- a/src/form.hh +++ b/src/form.hh @@ -48,16 +48,16 @@ void a_Html_form_display_hiddens2(void *v_form, bool display); */ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize); -void Html_tag_close_form(DilloHtml *html, int TagIdx); +void Html_tag_close_form(DilloHtml *html); void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize); void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize); -void Html_tag_close_textarea(DilloHtml *html, int TagIdx); +void Html_tag_close_textarea(DilloHtml *html); void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize); -void Html_tag_close_select(DilloHtml *html, int TagIdx); +void Html_tag_close_select(DilloHtml *html); void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize); -void Html_tag_close_button(DilloHtml *html, int TagIdx); +void Html_tag_close_button(DilloHtml *html); #endif /* __FORM_HH__ */ diff --git a/src/html.cc b/src/html.cc index ea675c1a..f1936f03 100644 --- a/src/html.cc +++ b/src/html.cc @@ -69,7 +69,7 @@ using namespace dw::core::style; *---------------------------------------------------------------------------*/ class DilloHtml; typedef void (*TagOpenFunct) (DilloHtml *html, const char *tag, int tagsize); -typedef void (*TagCloseFunct) (DilloHtml *html, int TagIdx); +typedef void (*TagCloseFunct) (DilloHtml *html); typedef enum { SEEK_ATTR_START, @@ -460,7 +460,7 @@ void DilloHtml::initDw() dReturn_if_fail (dw == NULL); /* Create the main widget */ - dw = stack->getRef(0)->textblock = new Textblock (prefs.limit_text_width); + dw = stack->getRef(0)->textblock = new Textblock (prefs.limit_text_width); bw->num_page_bugs = 0; dStr_truncate(bw->page_bugs, 0); @@ -1307,7 +1307,7 @@ static void Html_tag_cleanup_to_idx(DilloHtml *html, int idx) BUG_MSG(" - forcing close of open tag: <%s>\n", toptag.name); _MSG("Close: %*s%s\n", size," ", toptag.name); if (toptag.close) - toptag.close(html, toptag_idx); + toptag.close(html); Html_real_pop_tag(html); } } @@ -1573,7 +1573,7 @@ static void Html_tag_open_html(DilloHtml *html, const char *tag, int tagsize) /* * Handle close HTML element */ -static void Html_tag_close_html(DilloHtml *html, int TagIdx) +static void Html_tag_close_html(DilloHtml *html) { /* TODO: may add some checks here */ if (html->Num_HTML == 1) { @@ -1608,7 +1608,7 @@ static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize) * twice when the head element is closed implicitly. * Note2: HEAD is parsed once completely got. */ -static void Html_tag_close_head(DilloHtml *html, int TagIdx) +static void Html_tag_close_head(DilloHtml *html) { if (html->InFlags & IN_HEAD) { _MSG("Closing HEAD section\n"); @@ -1638,7 +1638,7 @@ static void Html_tag_open_title(DilloHtml *html, const char *tag, int tagsize) * Handle close TITLE * set page-title in the browser window and in the history. */ -static void Html_tag_close_title(DilloHtml *html, int TagIdx) +static void Html_tag_close_title(DilloHtml *html) { if (html->InFlags & IN_HEAD) { /* title is only valid inside HEAD */ @@ -1663,7 +1663,7 @@ static void Html_tag_open_script(DilloHtml *html, const char *tag, int tagsize) /* * Handle close SCRIPT */ -static void Html_tag_close_script(DilloHtml *html, int TagIdx) +static void Html_tag_close_script(DilloHtml *html) { /* eventually the stash will be sent to an interpreter for parsing */ } @@ -1700,7 +1700,7 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize) /* * Handle close STYLE */ -static void Html_tag_close_style(DilloHtml *html, int TagIdx) +static void Html_tag_close_style(DilloHtml *html) { if (prefs.parse_embedded_css && html->loadCssFromStash) html->styleEngine->parse(html, NULL, html->Stash->str, html->Stash->len, @@ -1790,7 +1790,7 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize) /* * BODY */ -static void Html_tag_close_body(DilloHtml *html, int TagIdx) +static void Html_tag_close_body(DilloHtml *html) { if (html->Num_BODY == 1) { /* some tag soup pages use multiple BODY tags... */ @@ -2226,7 +2226,7 @@ static void Html_tag_content_map(DilloHtml *html, const char *tag, int tagsize) /* * Handle close <MAP> */ -static void Html_tag_close_map(DilloHtml *html, int TagIdx) +static void Html_tag_close_map(DilloHtml *html) { /* This is a hack for the perhaps frivolous feature of drawing image map * shapes when there is no image to display. If this map is defined after @@ -2497,7 +2497,7 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize) /* * <A> close function */ -static void Html_tag_close_a(DilloHtml *html, int TagIdx) +static void Html_tag_close_a(DilloHtml *html) { html->InVisitedLink = false; } @@ -2529,7 +2529,7 @@ static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize) /* * </Q> */ -static void Html_tag_close_q(DilloHtml *html, int TagIdx) +static void Html_tag_close_q(DilloHtml *html) { /* Right Double Quotation Mark */ const char *U201D = "\xe2\x80\x9d"; @@ -2659,7 +2659,7 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize) /* * Close <LI> */ -static void Html_tag_close_li(DilloHtml *html, int TagIdx) +static void Html_tag_close_li(DilloHtml *html) { html->InFlags &= ~IN_LI; ((ListItem *)html->dw)->flush (); @@ -2771,7 +2771,7 @@ static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize) /* * Custom close for <PRE> */ -static void Html_tag_close_pre(DilloHtml *html, int TagIdx) +static void Html_tag_close_pre(DilloHtml *html) { html->InFlags &= ~IN_PRE; } @@ -3025,22 +3025,6 @@ static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize) } /* - * Set the history of the menu to be consistent with the active menuitem. - */ -//static void Html_select_set_history(DilloHtmlInput *input) -//{ -// int i; -// -// for (i = 0; i < input->select->num_options; i++) { -// if (GTK_CHECK_MENU_ITEM(input->select->options[i].menuitem)->active) { -// gtk_option_menu_set_history(GTK_OPTION_MENU(input->widget), i); -// break; -// } -// } -//} - - -/* * Set the Document Base URI */ static void Html_tag_open_base(DilloHtml *html, const char *tag, int tagsize) @@ -3109,7 +3093,7 @@ static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize) /* * Default close for paragraph tags - pop the stack and break. */ -static void Html_tag_close_par(DilloHtml *html, int TagIdx) +static void Html_tag_close_par(DilloHtml *html) { HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); } diff --git a/src/prefs.c b/src/prefs.c index a7fa1bcf..88d10a8d 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -101,6 +101,12 @@ void a_Prefs_init(void) prefs.start_page = a_Url_new(PREFS_START_PAGE, NULL); prefs.theme = dStrdup(PREFS_THEME); prefs.w3c_plus_heuristics = TRUE; + + prefs.penalty_hyphen = 100; + prefs.penalty_hyphen_2 = 100; + prefs.penalty_em_dash_left = 800; + prefs.penalty_em_dash_right = 100; + prefs.penalty_em_dash_right_2 = 800; } /* diff --git a/src/prefs.h b/src/prefs.h index 7622aea3..0c392ae5 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -89,6 +89,8 @@ struct _DilloPrefs { bool_t show_msg; bool_t show_extra_warnings; bool_t middle_click_drags_page; + int penalty_hyphen, penalty_hyphen_2; + int penalty_em_dash_left, penalty_em_dash_right, penalty_em_dash_right_2; }; /* Global Data */ diff --git a/src/prefsparser.cc b/src/prefsparser.cc index aa810b1e..6eb8c11d 100644 --- a/src/prefsparser.cc +++ b/src/prefsparser.cc @@ -12,6 +12,8 @@ #include <sys/types.h> #include <stdlib.h> #include <locale.h> /* for setlocale */ +#include <math.h> /* for isinf */ +#include <limits.h> #include "prefs.h" #include "misc.h" @@ -28,6 +30,7 @@ typedef enum { PREFS_URL, PREFS_INT32, PREFS_DOUBLE, + PREFS_FRACTION_100, PREFS_GEOMETRY, PREFS_PANEL_SIZE } PrefType_t; @@ -107,7 +110,15 @@ int PrefsParser::parseOption(char *name, char *value) { "small_icons", &prefs.small_icons, PREFS_BOOL }, { "start_page", &prefs.start_page, PREFS_URL }, { "theme", &prefs.theme, PREFS_STRING }, - { "w3c_plus_heuristics", &prefs.w3c_plus_heuristics, PREFS_BOOL } + { "w3c_plus_heuristics", &prefs.w3c_plus_heuristics, PREFS_BOOL }, + { "penalty_hyphen", &prefs.penalty_hyphen, PREFS_FRACTION_100 }, + { "penalty_hyphen_2", &prefs.penalty_hyphen_2, PREFS_FRACTION_100 }, + { "penalty_em_dash_left", &prefs.penalty_em_dash_left, + PREFS_FRACTION_100 }, + { "penalty_em_dash_right", &prefs.penalty_em_dash_right, + PREFS_FRACTION_100 }, + { "penalty_em_dash_right_2", &prefs.penalty_em_dash_right_2, + PREFS_FRACTION_100 } }; node = NULL; @@ -160,6 +171,18 @@ int PrefsParser::parseOption(char *name, char *value) case PREFS_DOUBLE: *(double *)node->pref = strtod(value, NULL); break; + case PREFS_FRACTION_100: + { + double d = strtod (value, NULL); + if (isinf(d)) { + if (d > 0) + *(int*)node->pref = INT_MAX; + else + *(int*)node->pref = INT_MIN; + } else + *(int*)node->pref = 100 * d; + } + break; case PREFS_GEOMETRY: a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos, &prefs.width, &prefs.height); diff --git a/src/styleengine.cc b/src/styleengine.cc index 48092c23..8aa8cdc3 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -51,7 +51,7 @@ StyleEngine::StyleEngine (dw::core::Layout *layout) { style_attrs.color = Color::create (layout, 0); style_attrs.backgroundColor = Color::create (layout, 0xffffff); - n->style = Style::create (layout, &style_attrs); + n->style = Style::create (&style_attrs); } StyleEngine::~StyleEngine () { @@ -718,7 +718,7 @@ Style * StyleEngine::backgroundStyle () { assert (attrs.backgroundColor); stack->getRef (stack->size () - 1)->backgroundStyle = - Style::create (layout, &attrs); + Style::create (&attrs); } return stack->getRef (stack->size () - 1)->backgroundStyle; } @@ -760,7 +760,7 @@ Style * StyleEngine::style0 (int i) { postprocessAttrs (&attrs); - stack->getRef (i)->style = Style::create (layout, &attrs); + stack->getRef (i)->style = Style::create (&attrs); return stack->getRef (i)->style; } @@ -774,7 +774,7 @@ Style * StyleEngine::wordStyle0 () { attrs.valign = style ()->valign; - stack->getRef(stack->size() - 1)->wordStyle = Style::create(layout, &attrs); + stack->getRef(stack->size() - 1)->wordStyle = Style::create(&attrs); return stack->getRef (stack->size () - 1)->wordStyle; } diff --git a/src/table.cc b/src/table.cc index 98157e76..41f2b686 100644 --- a/src/table.cc +++ b/src/table.cc @@ -297,7 +297,7 @@ static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb) collapseCellAttrs.borderWidth.bottom = borderWidth; collapseCellAttrs.hBorderSpacing = 0; collapseCellAttrs.vBorderSpacing = 0; - collapseStyle = Style::create(HT2LT(html), &collapseCellAttrs); + collapseStyle = Style::create(&collapseCellAttrs); col_tb->setStyle (collapseStyle); if (Html_table_get_border_model(html) != DILLO_HTML_TABLE_BORDER_COLLAPSE) { @@ -314,7 +314,7 @@ static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb) collapseTableAttrs.borderStyle = collapseCellAttrs.borderStyle; /* CSS2 17.6.2: table does not have padding (in collapsing mode) */ collapseTableAttrs.padding.setVal (0); - collapseStyle = Style::create(HT2LT(html), &collapseTableAttrs); + collapseStyle = Style::create(&collapseTableAttrs); ((dw::Table*)S_TOP(html)->table)->setStyle (collapseStyle); } } @@ -331,7 +331,7 @@ static void Html_set_separate_border_model(DilloHtml *html, Widget *col_tb) separateCellAttrs = *(html->styleEngine->style ()); /* CSS2 17.5: Internal table elements do not have margins */ separateCellAttrs.margin.setVal (0); - separateStyle = Style::create(HT2LT(html), &separateCellAttrs); + separateStyle = Style::create(&separateCellAttrs); col_tb->setStyle (separateStyle); } diff --git a/test/dw_anchors_test.cc b/test/dw_anchors_test.cc index cb839f40..d7201053 100644 --- a/test/dw_anchors_test.cc +++ b/test/dw_anchors_test.cc @@ -130,20 +130,20 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (5); styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - topWidgetStyle = Style::create (layout, &styleAttrs); + topWidgetStyle = Style::create (&styleAttrs); styleAttrs.margin.left = 20; styleAttrs.margin.right = 0; styleAttrs.backgroundColor = NULL; - widgetStyle = Style::create (layout, &styleAttrs); + widgetStyle = Style::create (&styleAttrs); styleAttrs.margin.left = 0; - wordStyle = Style::create (layout, &styleAttrs); + wordStyle = Style::create (&styleAttrs); fontAttrs.size = 28; fontAttrs.weight = 700; styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs); - headingStyle = Style::create (layout, &styleAttrs); + headingStyle = Style::create (&styleAttrs); Fl::add_timeout (0, textTimeout, NULL); diff --git a/test/dw_border_test.cc b/test/dw_border_test.cc index 988abc33..ec5a09a5 100644 --- a/test/dw_border_test.cc +++ b/test/dw_border_test.cc @@ -65,7 +65,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle1 = Style::create (layout, &styleAttrs); + Style *widgetStyle1 = Style::create (&styleAttrs); styleAttrs.backgroundColor = Color::create (layout, 0xffff80); styleAttrs.margin.setVal (0); @@ -74,7 +74,7 @@ int main(int argc, char **argv) styleAttrs.setBorderStyle (BORDER_SOLID); styleAttrs.padding.setVal (1); - Style *widgetStyle2 = Style::create (layout, &styleAttrs); + Style *widgetStyle2 = Style::create (&styleAttrs); Textblock *textblock1 = new Textblock (false); textblock1->setStyle (widgetStyle1); @@ -87,7 +87,7 @@ int main(int argc, char **argv) styleAttrs.backgroundColor = NULL; styleAttrs.cursor = CURSOR_TEXT; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); const char *words1[] = { "Some", "random", "text.", NULL }; const char *words2[] = { "A", "nested", "paragraph.", NULL }; diff --git a/test/dw_example.cc b/test/dw_example.cc index 52aa2440..259650cf 100644 --- a/test/dw_example.cc +++ b/test/dw_example.cc @@ -60,7 +60,7 @@ int main(int argc, char **argv) dw::core::style::Color::create (layout, 0xffffff); dw::core::style::Style *widgetStyle = - dw::core::style::Style::create (layout, &styleAttrs); + dw::core::style::Style::create (&styleAttrs); dw::Textblock *textblock = new dw::Textblock (false); textblock->setStyle (widgetStyle); @@ -72,7 +72,7 @@ int main(int argc, char **argv) styleAttrs.backgroundColor = NULL; dw::core::style::Style *wordStyle = - dw::core::style::Style::create (layout, &styleAttrs); + dw::core::style::Style::create (&styleAttrs); for(int i = 1; i <= 10; i++) { char buf[4]; diff --git a/test/dw_find_test.cc b/test/dw_find_test.cc index e5c79fd3..5bddda10 100644 --- a/test/dw_find_test.cc +++ b/test/dw_find_test.cc @@ -107,15 +107,15 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (10); styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *topWidgetStyle = Style::create (layout, &styleAttrs); + Style *topWidgetStyle = Style::create (&styleAttrs); styleAttrs.margin.setVal (0); styleAttrs.margin.left = 30; styleAttrs.backgroundColor = NULL; - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); styleAttrs.margin.left = 0; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (topWidgetStyle); diff --git a/test/dw_float_test.cc b/test/dw_float_test.cc index 11d04984..70273e89 100644 --- a/test/dw_float_test.cc +++ b/test/dw_float_test.cc @@ -58,18 +58,18 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); styleAttrs.borderWidth.setVal (1); styleAttrs.setBorderColor (Color::create (layout, 0x808080)); styleAttrs.setBorderStyle (BORDER_DASHED); styleAttrs.width = createAbsLength(100); styleAttrs.vloat = FLOAT_LEFT; - Style *leftFloatStyle = Style::create (layout, &styleAttrs); + Style *leftFloatStyle = Style::create (&styleAttrs); styleAttrs.width = createAbsLength(80); styleAttrs.vloat = FLOAT_RIGHT; - Style *rightFloatStyle = Style::create (layout, &styleAttrs); + Style *rightFloatStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -83,7 +83,7 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (0); styleAttrs.backgroundColor = NULL; - wordStyle = Style::create (layout, &styleAttrs); + wordStyle = Style::create (&styleAttrs); for(int i = 1; i <= 10; i++) { char buf[16]; diff --git a/test/dw_images_scaled.cc b/test/dw_images_scaled.cc index 2f8896e6..34a72c21 100644 --- a/test/dw_images_scaled.cc +++ b/test/dw_images_scaled.cc @@ -120,7 +120,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -131,7 +131,7 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (0); styleAttrs.backgroundColor = NULL; - Style *imageStyle = Style::create (layout, &styleAttrs); + Style *imageStyle = Style::create (&styleAttrs); image = new dw::Image (""); textblock->addWidget (image, imageStyle); diff --git a/test/dw_images_scaled2.cc b/test/dw_images_scaled2.cc index 2adb1770..81cdd2e8 100644 --- a/test/dw_images_scaled2.cc +++ b/test/dw_images_scaled2.cc @@ -95,7 +95,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -108,7 +108,7 @@ int main(int argc, char **argv) styleAttrs.padding.setVal (0); styleAttrs.backgroundColor = NULL; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); styleAttrs.borderWidth.setVal (1); styleAttrs.setBorderColor (Color::create (layout, 0x000080)); @@ -118,7 +118,7 @@ int main(int argc, char **argv) styleAttrs.width = createPerLength (0.25); styleAttrs.height = createPerLength (0.25); - Style *imageStyle1 = Style::create (layout, &styleAttrs); + Style *imageStyle1 = Style::create (&styleAttrs); image1 = new dw::Image ("A longer ALT Text to demonstrate clipping."); textblock->addWidget (image1, imageStyle1); imageStyle1->unref(); @@ -128,7 +128,7 @@ int main(int argc, char **argv) styleAttrs.width = LENGTH_AUTO; styleAttrs.height = LENGTH_AUTO; - Style *imageStyle2 = Style::create (layout, &styleAttrs); + Style *imageStyle2 = Style::create (&styleAttrs); image2 = new dw::Image ("A longer ALT Text to demonstrate clipping."); textblock->addWidget (image2, imageStyle2); imageStyle2->unref(); diff --git a/test/dw_images_simple.cc b/test/dw_images_simple.cc index 8f00f847..34de20ea 100644 --- a/test/dw_images_simple.cc +++ b/test/dw_images_simple.cc @@ -119,7 +119,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -130,7 +130,7 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (0); styleAttrs.backgroundColor = NULL; - Style *imageStyle = Style::create (layout, &styleAttrs); + Style *imageStyle = Style::create (&styleAttrs); image = new dw::Image (""); textblock->addWidget (image, imageStyle); diff --git a/test/dw_links.cc b/test/dw_links.cc index 5622cbd5..79ccbb54 100644 --- a/test/dw_links.cc +++ b/test/dw_links.cc @@ -99,7 +99,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -113,7 +113,7 @@ int main(int argc, char **argv) styleAttrs.backgroundColor = NULL; styleAttrs.cursor = CURSOR_TEXT; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); styleAttrs.color = Color::create (layout, 0x0000ff); styleAttrs.textDecoration = TEXT_DECORATION_UNDERLINE; @@ -137,7 +137,7 @@ int main(int argc, char **argv) } styleAttrs.x_link = i; - Style *linkStyle = Style::create (layout, &styleAttrs); + Style *linkStyle = Style::create (&styleAttrs); for(int j = 0; words2[j]; j++) { textblock->addText(words2[j], linkStyle); diff --git a/test/dw_links2.cc b/test/dw_links2.cc index b8e8c55b..acb095f3 100644 --- a/test/dw_links2.cc +++ b/test/dw_links2.cc @@ -128,7 +128,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -142,7 +142,7 @@ int main(int argc, char **argv) styleAttrs.backgroundColor = NULL; styleAttrs.cursor = CURSOR_TEXT; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); styleAttrs.color = Color::create (layout, 0x0000ff); styleAttrs.textDecoration = TEXT_DECORATION_UNDERLINE; @@ -166,7 +166,7 @@ int main(int argc, char **argv) } styleAttrs.x_link = i; - Style *linkStyle = Style::create (layout, &styleAttrs); + Style *linkStyle = Style::create (&styleAttrs); for(int j = 0; words2[j]; j++) { textblock->addText (words2[j], linkStyle); diff --git a/test/dw_lists.cc b/test/dw_lists.cc index 12fa1627..2aa0abb7 100644 --- a/test/dw_lists.cc +++ b/test/dw_lists.cc @@ -61,7 +61,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -73,7 +73,7 @@ int main(int argc, char **argv) styleAttrs.backgroundColor = NULL; styleAttrs.cursor = CURSOR_TEXT; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); styleAttrs.margin.setVal (5); styleAttrs.padding.setVal (5); @@ -82,7 +82,7 @@ int main(int argc, char **argv) styleAttrs.setBorderStyle (BORDER_SOLID); styleAttrs.borderWidth.setVal (1); - Style *itemStyle = Style::create (layout, &styleAttrs); + Style *itemStyle = Style::create (&styleAttrs); const char *wordsPar[] = { "This", "is", "a", "normal", "paragraph.", "And", diff --git a/test/dw_resource_test.cc b/test/dw_resource_test.cc index a2a26c62..e8a8b227 100644 --- a/test/dw_resource_test.cc +++ b/test/dw_resource_test.cc @@ -62,7 +62,7 @@ int main(int argc, char **argv) styleAttrs.color = Color::create (layout, 0x000000); styleAttrs.backgroundColor = Color::create (layout, 0xffffff); - Style *widgetStyle = Style::create (layout, &styleAttrs); + Style *widgetStyle = Style::create (&styleAttrs); Textblock *textblock = new Textblock (false); textblock->setStyle (widgetStyle); @@ -73,7 +73,7 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (0); styleAttrs.backgroundColor = NULL; - widgetStyle = Style::create (layout, &styleAttrs); + widgetStyle = Style::create (&styleAttrs); SelectionResource *res = layout->getResourceFactory()->createListResource (ListResource::SELECTION_AT_MOST_ONE, 4); diff --git a/test/dw_table.cc b/test/dw_table.cc index 75842e60..5416d05b 100644 --- a/test/dw_table.cc +++ b/test/dw_table.cc @@ -66,7 +66,7 @@ int main(int argc, char **argv) fontAttrs.fontVariant = FONT_VARIANT_NORMAL; styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs); - Style *tableStyle = Style::create (layout, &styleAttrs); + Style *tableStyle = Style::create (&styleAttrs); Table *table = new Table (false); table->setStyle (tableStyle); @@ -79,14 +79,14 @@ int main(int argc, char **argv) styleAttrs.margin.setVal (0); styleAttrs.padding.setVal (5); - Style *cellStyle = Style::create (layout, &styleAttrs); + Style *cellStyle = Style::create (&styleAttrs); styleAttrs.borderWidth.setVal (0); styleAttrs.margin.setVal (0); styleAttrs.cursor = CURSOR_TEXT; styleAttrs.textAlignChar = '.'; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); for (int i = 0; i < 4; i++) { table->addRow (wordStyle); diff --git a/test/dw_table_aligned.cc b/test/dw_table_aligned.cc index 022e7026..96cb0602 100644 --- a/test/dw_table_aligned.cc +++ b/test/dw_table_aligned.cc @@ -66,7 +66,7 @@ int main(int argc, char **argv) styleAttrs.hBorderSpacing = 5; styleAttrs.vBorderSpacing = 5; - Style *tableStyle = Style::create (layout, &styleAttrs); + Style *tableStyle = Style::create (&styleAttrs); Table *table = new Table (false); table->setStyle (tableStyle); @@ -77,7 +77,7 @@ int main(int argc, char **argv) styleAttrs.borderWidth.setVal (1); styleAttrs.setBorderStyle (BORDER_INSET); - Style *cellStyle = Style::create (layout, &styleAttrs); + Style *cellStyle = Style::create (&styleAttrs); styleAttrs.borderWidth.setVal (0); styleAttrs.margin.setVal (0); @@ -85,7 +85,7 @@ int main(int argc, char **argv) styleAttrs.cursor = CURSOR_TEXT; styleAttrs.textAlignChar = '.'; - Style *wordStyle = Style::create (layout, &styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); TableCell *ref = NULL; for(int i = 0; i < 10; i++) { diff --git a/test/dw_ui_test.cc b/test/dw_ui_test.cc index 60893f06..2bcee1c5 100644 --- a/test/dw_ui_test.cc +++ b/test/dw_ui_test.cc @@ -65,7 +65,7 @@ int main(int argc, char **argv) fontAttrs.fontVariant = FONT_VARIANT_NORMAL; styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs); - Style *tableStyle = Style::create (layout, &styleAttrs); + Style *tableStyle = Style::create (&styleAttrs); Table *table = new Table (false); table->setStyle (tableStyle); @@ -76,7 +76,7 @@ int main(int argc, char **argv) styleAttrs.backgroundColor = NULL; styleAttrs.margin.setVal (0); - Style *cellStyle = Style::create (layout, &styleAttrs); + Style *cellStyle = Style::create (&styleAttrs); // First of all, the resources. Later, they are embedded into the // widget tree. diff --git a/test/hyphens-etc.html b/test/hyphens-etc.html new file mode 100644 index 00000000..cbc55b79 --- /dev/null +++ b/test/hyphens-etc.html @@ -0,0 +1,6 @@ +<p>Abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde­abcde</p> +<p>Abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde-abcde</p> +<p>Abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde‐abcde</p> +<p>Abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde—abcde</p> +<p lang="de">Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen—Nordrhein-Westfalen</p> +<p lang="de">Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen—Nord­rheinwestfalen</p> |