You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							270 lines
						
					
					
						
							8.6 KiB
						
					
					
				
			
		
		
	
	
							270 lines
						
					
					
						
							8.6 KiB
						
					
					
				/** | 
						|
 * Word wrapping | 
						|
 *  | 
						|
 * @author (Javascript) Dmitry Farafonov | 
						|
 */ | 
						|
 | 
						|
		var AttributedStringIterator = function(text){ | 
						|
				//this.text = this.rtrim(this.ltrim(text)); | 
						|
				text = text.replace(/(\s)+/, " "); | 
						|
				this.text = this.rtrim(text); | 
						|
				/* | 
						|
				if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) { | 
						|
					throw new IllegalArgumentException("Invalid substring range"); | 
						|
				} | 
						|
				*/ | 
						|
				this.beginIndex = 0; | 
						|
				this.endIndex = this.text.length; | 
						|
				this.currentIndex = this.beginIndex; | 
						|
				 | 
						|
				//console.group("[AttributedStringIterator]"); | 
						|
				var i = 0; | 
						|
				var string = this.text; | 
						|
				var fullPos = 0; | 
						|
				 | 
						|
				//console.log("string: \"" + string + "\", length: " + string.length); | 
						|
				this.startWordOffsets = []; | 
						|
				this.startWordOffsets.push(fullPos); | 
						|
				 | 
						|
				// TODO: remove i 1000 | 
						|
				while (i<1000) { | 
						|
					var pos = string.search(/[ \t\n\f-\.\,]/); | 
						|
					if (pos == -1) | 
						|
						break; | 
						|
					 | 
						|
					// whitespace start | 
						|
					fullPos += pos; | 
						|
					string = string.substr(pos); | 
						|
					////console.log("fullPos: " + fullPos + ", pos: " + pos +  ", string: ", string); | 
						|
					 | 
						|
					// remove whitespaces | 
						|
					var pos = string.search(/[^ \t\n\f-\.\,]/); | 
						|
					if (pos == -1) | 
						|
						break; | 
						|
						 | 
						|
					// whitespace end | 
						|
					fullPos += pos; | 
						|
					string = string.substr(pos); | 
						|
					 | 
						|
					////console.log("fullPos: " + fullPos); | 
						|
					this.startWordOffsets.push(fullPos); | 
						|
					 | 
						|
					i++; | 
						|
				} | 
						|
				//console.log("startWordOffsets: ", this.startWordOffsets); | 
						|
				//console.groupEnd(); | 
						|
			}; | 
						|
			AttributedStringIterator.prototype = { | 
						|
				getEndIndex: function(pos){ | 
						|
					if (typeof(pos) == "undefined") | 
						|
						return this.endIndex; | 
						|
						 | 
						|
					var string = this.text.substr(pos, this.endIndex - pos); | 
						|
					 | 
						|
					var posEndOfLine = string.search(/[\n]/); | 
						|
					if (posEndOfLine == -1) | 
						|
						return this.endIndex; | 
						|
					else | 
						|
						return pos + posEndOfLine; | 
						|
				}, | 
						|
				getBeginIndex: function(){ | 
						|
					return this.beginIndex; | 
						|
				}, | 
						|
				isWhitespace: function(pos){ | 
						|
					var str = this.text[pos]; | 
						|
					var whitespaceChars = " \t\n\f"; | 
						|
					 | 
						|
					return (whitespaceChars.indexOf(str) != -1); | 
						|
				}, | 
						|
				isNewLine: function(pos){ | 
						|
					var str = this.text[pos]; | 
						|
					var whitespaceChars = "\n"; | 
						|
					 | 
						|
					return (whitespaceChars.indexOf(str) != -1); | 
						|
				}, | 
						|
				preceding: function(pos){ | 
						|
					//console.group("[AttributedStringIterator.preceding]"); | 
						|
					for(var i in this.startWordOffsets) { | 
						|
						var startWordOffset = this.startWordOffsets[i]; | 
						|
						if (pos < startWordOffset && i>0) { | 
						|
							//console.log("startWordOffset: " + this.startWordOffsets[i-1]); | 
						|
							//console.groupEnd(); | 
						|
							return this.startWordOffsets[i-1]; | 
						|
						} | 
						|
					} | 
						|
					//console.log("pos: " + pos); | 
						|
					//console.groupEnd(); | 
						|
					return this.startWordOffsets[i]; | 
						|
				}, | 
						|
				following: function(pos){ | 
						|
					//console.group("[AttributedStringIterator.following]"); | 
						|
					for(var i in this.startWordOffsets) { | 
						|
						var startWordOffset = this.startWordOffsets[i]; | 
						|
						if (pos < startWordOffset && i>0) { | 
						|
							//console.log("startWordOffset: " + this.startWordOffsets[i]); | 
						|
							//console.groupEnd(); | 
						|
							return this.startWordOffsets[i]; | 
						|
						} | 
						|
					} | 
						|
					//console.log("pos: " + pos); | 
						|
					//console.groupEnd(); | 
						|
					return this.startWordOffsets[i]; | 
						|
				}, | 
						|
				ltrim: function(str){ | 
						|
					var patt2=/^\s+/g; | 
						|
					return str.replace(patt2, ""); | 
						|
				},  | 
						|
				rtrim: function(str){ | 
						|
					var patt2=/\s+$/g; | 
						|
					return str.replace(patt2, ""); | 
						|
				}, | 
						|
				getLayout: function(start, limit){ | 
						|
					return this.text.substr(start, limit - start); | 
						|
				}, | 
						|
				getCharAtPos: function(pos) { | 
						|
					return this.text[pos]; | 
						|
				} | 
						|
			}; | 
						|
 | 
						|
		var LineBreakMeasurer = function(paper, x, y, text, fontAttrs){ | 
						|
				this.paper = paper; | 
						|
				this.text = new AttributedStringIterator(text); | 
						|
				this.fontAttrs = fontAttrs; | 
						|
				 | 
						|
				if (this.text.getEndIndex() - this.text.getBeginIndex() < 1) { | 
						|
					throw {message: "Text must contain at least one character.", code: "IllegalArgumentException"}; | 
						|
				} | 
						|
				 | 
						|
				//this.measurer = new TextMeasurer(paper, this.text, this.fontAttrs); | 
						|
				this.limit = this.text.getEndIndex(); | 
						|
				this.pos = this.start = this.text.getBeginIndex(); | 
						|
				 | 
						|
				this.rafaelTextObject = this.paper.text(x, y, this.text.text).attr(fontAttrs).attr("text-anchor", "start"); | 
						|
				this.svgTextObject = this.rafaelTextObject[0]; | 
						|
			}; | 
						|
			LineBreakMeasurer.prototype = { | 
						|
				nextOffset: function(wrappingWidth, offsetLimit, requireNextWord) { | 
						|
					//console.group("[nextOffset]"); | 
						|
					var nextOffset = this.pos; | 
						|
					if (this.pos < this.limit) { | 
						|
						if (offsetLimit <= this.pos) { | 
						|
							throw {message: "offsetLimit must be after current position", code: "IllegalArgumentException"}; | 
						|
						} | 
						|
						 | 
						|
						var charAtMaxAdvance = this.getLineBreakIndex(this.pos, wrappingWidth); | 
						|
						//charAtMaxAdvance --; | 
						|
						//console.log("charAtMaxAdvance:", charAtMaxAdvance, ", [" + this.text.getCharAtPos(charAtMaxAdvance) + "]"); | 
						|
						 | 
						|
						if (charAtMaxAdvance == this.limit) { | 
						|
							nextOffset = this.limit; | 
						|
							//console.log("charAtMaxAdvance == this.limit"); | 
						|
						} else if (this.text.isNewLine(charAtMaxAdvance)) { | 
						|
							//console.log("isNewLine"); | 
						|
							nextOffset = charAtMaxAdvance+1; | 
						|
						} else if (this.text.isWhitespace(charAtMaxAdvance)) { | 
						|
							// TODO: find next noSpaceChar | 
						|
							//return nextOffset; | 
						|
							nextOffset = this.text.following(charAtMaxAdvance); | 
						|
						} else { | 
						|
							// Break is in a word;  back up to previous break. | 
						|
							/* | 
						|
							var testPos = charAtMaxAdvance + 1; | 
						|
							if (testPos == this.limit) { | 
						|
								console.error("hbz..."); | 
						|
							} else { | 
						|
								nextOffset = this.text.preceding(charAtMaxAdvance); | 
						|
							} | 
						|
							*/ | 
						|
							nextOffset = this.text.preceding(charAtMaxAdvance); | 
						|
							 | 
						|
							if (nextOffset <= this.pos) { | 
						|
								nextOffset = Math.max(this.pos+1, charAtMaxAdvance); | 
						|
							} | 
						|
						} | 
						|
					} | 
						|
					if (nextOffset > offsetLimit) { | 
						|
						nextOffset = offsetLimit; | 
						|
					} | 
						|
					//console.log("nextOffset: " + nextOffset); | 
						|
					//console.groupEnd(); | 
						|
					return nextOffset; | 
						|
				}, | 
						|
				nextLayout: function(wrappingWidth) { | 
						|
					//console.groupCollapsed("[nextLayout]"); | 
						|
					if (this.pos < this.limit) { | 
						|
						var requireNextWord = false; | 
						|
						var layoutLimit = this.nextOffset(wrappingWidth, this.limit, requireNextWord); | 
						|
						//console.log("layoutLimit:", layoutLimit); | 
						|
						if (layoutLimit == this.pos) { | 
						|
							//console.groupEnd(); | 
						|
							return null; | 
						|
						} | 
						|
						var result = this.text.getLayout(this.pos, layoutLimit); | 
						|
						//console.log("layout: \"" + result + "\""); | 
						|
						 | 
						|
						// remove end of line | 
						|
						 | 
						|
						//var posEndOfLine = this.text.getEndIndex(this.pos); | 
						|
						//if (posEndOfLine < result.length) | 
						|
						//	result = result.substr(0, posEndOfLine); | 
						|
						 | 
						|
						this.pos = layoutLimit; | 
						|
						 | 
						|
						//console.groupEnd(); | 
						|
						return result; | 
						|
					} else { | 
						|
						//console.groupEnd(); | 
						|
						return null; | 
						|
					} | 
						|
				}, | 
						|
				getLineBreakIndex: function(pos, wrappingWidth) { | 
						|
					//console.group("[getLineBreakIndex]"); | 
						|
					//console.log("pos:"+pos + ", text: \""+ this.text.text.replace(/\n/g, "_").substr(pos, 1) + "\""); | 
						|
					 | 
						|
					var bb = this.rafaelTextObject.getBBox(); | 
						|
					 | 
						|
					var charNum = -1; | 
						|
					try { | 
						|
						var svgPoint = this.svgTextObject.getStartPositionOfChar(pos); | 
						|
						//var dot = this.paper.ellipse(svgPoint.x, svgPoint.y, 1, 1).attr({"stroke-width": 0, fill: Color.blue}); | 
						|
						svgPoint.x = svgPoint.x + wrappingWidth; | 
						|
						//svgPoint.y = bb.y; | 
						|
						//console.log("svgPoint:", svgPoint); | 
						|
					 | 
						|
						//var dot = this.paper.ellipse(svgPoint.x, svgPoint.y, 1, 1).attr({"stroke-width": 0, fill: Color.red}); | 
						|
					 | 
						|
						charNum = this.svgTextObject.getCharNumAtPosition(svgPoint); | 
						|
					} catch (e){ | 
						|
						console.warn("getStartPositionOfChar error, pos:" + pos); | 
						|
						/* | 
						|
						var testPos = pos + 1; | 
						|
						if (testPos < this.limit) { | 
						|
							return testPos | 
						|
						} | 
						|
						*/ | 
						|
					} | 
						|
					//console.log("charNum:", charNum); | 
						|
					if (charNum == -1) { | 
						|
						//console.groupEnd(); | 
						|
						return this.text.getEndIndex(pos); | 
						|
					} else { | 
						|
						// When case there is new line between pos and charnum then use this new line | 
						|
						var newLineIndex = this.text.getEndIndex(pos); | 
						|
						if (newLineIndex < charNum ) { | 
						|
							console.log("newLineIndex <= charNum, newLineIndex:"+newLineIndex+", charNum:"+charNum, "\"" + this.text.text.substr(newLineIndex+1).replace(/\n/g, "?") + "\""); | 
						|
							//console.groupEnd(); | 
						|
							 | 
						|
							return newLineIndex; | 
						|
						} | 
						|
							 | 
						|
						//var charAtMaxAdvance  = this.text.text.substring(charNum, charNum + 1); | 
						|
						var charAtMaxAdvance  = this.text.getCharAtPos(charNum); | 
						|
						//console.log("!!charAtMaxAdvance: " + charAtMaxAdvance); | 
						|
						//console.groupEnd(); | 
						|
						return charNum; | 
						|
					} | 
						|
				},  | 
						|
				getPosition: function() { | 
						|
					return this.pos; | 
						|
				} | 
						|
			}; |